Make StackTrace generally not nullable in migrated NNBD library.

Bug: https://github.com/dart-lang/sdk/issues/40130
Change-Id: I13aba0c2a3fa5b1c3d3995f075ffd38f03aca897
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/139880
Reviewed-by: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
Leaf Petersen 2020-03-19 23:31:30 +00:00
parent 3f8366363b
commit 7aace6fa60
37 changed files with 279 additions and 188 deletions

View file

@ -6,8 +6,18 @@
### Core libraries
#### `dart:async`
* Make stack traces non-null. Where methods like `completer.completeError`
allows omitting a stack trace, the platform will now insert a default
stack trace rather than propagate a `null` value.
Error handling functions need no longer be prepared for `null` stack traces.
#### `dart:core`
* Adds `StackTrace.empty` constant which is the stack trace used as default
stack trace when no better alternative is available.
* The class `TypeError` no longer extends `AssertionError`.
This also means that it no longer inherits the spurious `message` getter
which was added to `AssertionError` when the second operand to `assert`

View file

@ -647,7 +647,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#26 Future._completeError ',
r'^#27 _AsyncAwaitCompleter.completeError ',
r'^#28 allYield ',
r'^#29 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#29 _asyncErrorWrapperHelper.errorCallback ',
r'^#30 _RootZone.runBinary ',
r'^#31 _FutureListener.handleError ',
r'^#32 Future._propagateToListeners.handleError ',
@ -655,7 +655,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#34 Future._completeError ',
r'^#35 _AsyncAwaitCompleter.completeError ',
r'^#36 allYield2 ',
r'^#37 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#37 _asyncErrorWrapperHelper.errorCallback ',
r'^#38 _RootZone.runBinary ',
r'^#39 _FutureListener.handleError ',
r'^#40 Future._propagateToListeners.handleError ',
@ -691,7 +691,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#19 _AsyncAwaitCompleter.complete ',
r'^#20 _completeOnAsyncReturn ',
r'^#21 doTestAwait ',
r'^#22 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#22 _asyncErrorWrapperHelper.errorCallback ',
r'^#23 _RootZone.runBinary ',
r'^#24 _FutureListener.handleError ',
r'^#25 Future._propagateToListeners.handleError ',
@ -699,7 +699,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#27 Future._completeError ',
r'^#28 _AsyncAwaitCompleter.completeError ',
r'^#29 noYields ',
r'^#30 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#30 _asyncErrorWrapperHelper.errorCallback ',
r'^#31 _RootZone.runBinary ',
r'^#32 _FutureListener.handleError ',
r'^#33 Future._propagateToListeners.handleError ',
@ -707,7 +707,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#35 Future._completeError ',
r'^#36 _AsyncAwaitCompleter.completeError ',
r'^#37 noYields2 ',
r'^#38 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#38 _asyncErrorWrapperHelper.errorCallback ',
r'^#39 _RootZone.runBinary ',
r'^#40 _FutureListener.handleError ',
r'^#41 Future._propagateToListeners.handleError ',
@ -737,7 +737,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#19 _AsyncAwaitCompleter.complete ',
r'^#20 _completeOnAsyncReturn ',
r'^#21 doTestAwaitThen ',
r'^#22 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#22 _asyncErrorWrapperHelper.errorCallback ',
r'^#23 _RootZone.runBinary ',
r'^#24 _FutureListener.handleError ',
r'^#25 Future._propagateToListeners.handleError ',
@ -745,7 +745,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#27 Future._completeError ',
r'^#28 _AsyncAwaitCompleter.completeError ',
r'^#29 noYields ',
r'^#30 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#30 _asyncErrorWrapperHelper.errorCallback ',
r'^#31 _RootZone.runBinary ',
r'^#32 _FutureListener.handleError ',
r'^#33 Future._propagateToListeners.handleError ',
@ -753,7 +753,7 @@ Future<void> doTestsNoCausalNoLazy() async {
r'^#35 Future._completeError ',
r'^#36 _AsyncAwaitCompleter.completeError ',
r'^#37 noYields2 ',
r'^#38 _asyncErrorWrapperHelper.<anonymous closure> ',
r'^#38 _asyncErrorWrapperHelper.errorCallback ',
r'^#39 _RootZone.runBinary ',
r'^#40 _FutureListener.handleError ',
r'^#41 Future._propagateToListeners.handleError ',

View file

@ -205,7 +205,8 @@ class _AsyncAwaitCompleter<T> implements Completer<T> {
}
}
void completeError(e, [st]) {
void completeError(Object e, [StackTrace st]) {
st ??= AsyncError.defaultStackTrace(e);
if (isSync) {
_future._completeError(e, st);
} else {

View file

@ -35,7 +35,8 @@ class _AsyncAwaitCompleter<T> implements Completer<T> {
}
}
void completeError(e, [st]) {
void completeError(Object e, [StackTrace st]) {
st ??= AsyncError.defaultStackTrace(e);
if (isSync) {
_future._completeError(e, st);
} else {
@ -79,7 +80,7 @@ Function _asyncThenWrapperHelper(continuation) {
// parameter to the continuation. See vm/ast_transformer.cc for usage.
Function _asyncErrorWrapperHelper(continuation) {
// See comments of `_asyncThenWrapperHelper`.
var errorCallback = (e, s) => continuation(null, e, s);
void errorCallback(Object e, StackTrace s) => continuation(null, e, s);
if (Zone.current == Zone.root) return errorCallback;
return Zone.current.registerBinaryCallback(errorCallback);
}

View file

@ -260,6 +260,7 @@ abstract class _BroadcastStreamController<T>
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
stackTrace ??= AsyncError.defaultStackTrace(error);
_sendError(error, stackTrace);
}
@ -481,6 +482,7 @@ class _AsBroadcastStreamController<T> extends _SyncBroadcastStreamController<T>
void addError(Object error, [StackTrace stackTrace]) {
ArgumentError.checkNotNull(error, "error");
stackTrace ??= AsyncError.defaultStackTrace(error);
if (!isClosed && _isFiring) {
_addPendingEvent(new _DelayedError(error, stackTrace));
return;

View file

@ -280,6 +280,7 @@ abstract class Future<T> {
stackTrace = replacement.stackTrace;
}
}
stackTrace ??= AsyncError.defaultStackTrace(error);
return new _Future<T>.immediateError(error, stackTrace);
}
@ -363,7 +364,7 @@ abstract class Future<T> {
// Handle an error from any of the futures.
// TODO(jmesserly): use `void` return type once it can be inferred for the
// `then` call below.
handleError(theError, StackTrace theStackTrace) {
handleError(Object theError, StackTrace theStackTrace) {
remaining--;
if (values != null) {
if (cleanUp != null) {
@ -905,13 +906,14 @@ abstract class Completer<T> {
}
// Helper function completing a _Future with error, but checking the zone
// for error replacement first.
// for error replacement first and missing stack trace.
void _completeWithErrorCallback(_Future result, error, StackTrace stackTrace) {
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
stackTrace ??= AsyncError.defaultStackTrace(error);
result._completeError(error, stackTrace);
}
@ -923,6 +925,7 @@ void _asyncCompleteWithErrorCallback(
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
stackTrace ??= AsyncError.defaultStackTrace(error);
result._asyncCompleteError(error, stackTrace);
}

View file

@ -26,6 +26,7 @@ abstract class _Completer<T> implements Completer<T> {
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
stackTrace ??= AsyncError.defaultStackTrace(error);
_completeError(error, stackTrace);
}
@ -229,7 +230,7 @@ class _Future<T> implements Future<T> {
_setValue(value);
}
_Future.immediateError(var error, [StackTrace stackTrace])
_Future.immediateError(var error, StackTrace stackTrace)
: _zone = Zone.current {
_asyncCompleteError(error, stackTrace);
}

View file

@ -156,7 +156,7 @@ abstract class Stream<T> {
factory Stream.error(Object error, [StackTrace stackTrace]) {
ArgumentError.checkNotNull(error, "error");
return (_AsyncStreamController<T>(null, null, null, null)
.._addError(error, stackTrace)
.._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error))
.._closeUnchecked())
.stream;
}

View file

@ -611,6 +611,7 @@ abstract class _StreamController<T> implements _StreamControllerBase<T> {
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
stackTrace ??= AsyncError.defaultStackTrace(error);
_addError(error, stackTrace);
}

View file

@ -18,7 +18,7 @@ class _EventSinkWrapper<T> implements EventSink<T> {
}
void addError(error, [StackTrace stackTrace]) {
_sink._addError(error, stackTrace);
_sink._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error));
}
void close() {
@ -239,6 +239,7 @@ class _HandlerEventSink<S, T> implements EventSink<S> {
throw StateError("Sink is closed");
}
if (_handleError != null) {
stackTrace ??= AsyncError.defaultStackTrace(error);
_handleError(error, stackTrace, _sink);
} else {
_sink.addError(error, stackTrace);

View file

@ -43,10 +43,24 @@ class AsyncError implements Error {
final Object error;
final StackTrace stackTrace;
AsyncError(this.error, this.stackTrace) {
AsyncError(this.error, StackTrace stackTrace)
: stackTrace = stackTrace ?? defaultStackTrace(error) {
ArgumentError.checkNotNull(error, "error");
}
/// A default stack trace for an error.
///
/// If [error] is an [Error] and it has an [Error.stackTrace],
/// that stack trace is returned.
/// If not, the [StackTrace.empty] default stack trace is returned.
static StackTrace defaultStackTrace(Object error) {
if (error is Error) {
var stackTrace = error.stackTrace;
if (stackTrace != null) return stackTrace;
}
return StackTrace.empty;
}
String toString() => '$error';
}

View file

@ -16,6 +16,13 @@ part of dart.core;
* them programmatically.
*/
abstract class StackTrace {
/// A stack trace object with no information.
///
/// This stack trace is used as the default in situations where
/// a stack trace is required, but the user has not supplied one.
@Since("2.8")
static const empty = const _StringStackTrace("");
StackTrace(); // In case existing classes extend StackTrace.
/**
@ -57,6 +64,6 @@ abstract class StackTrace {
class _StringStackTrace implements StackTrace {
final String _stackTrace;
_StringStackTrace(this._stackTrace);
const _StringStackTrace(this._stackTrace);
String toString() => _stackTrace;
}

View file

@ -666,7 +666,7 @@ class _StreamSinkImpl<T> implements StreamSink<T> {
}
}
void _completeDoneError(Object error, StackTrace? stackTrace) {
void _completeDoneError(Object error, StackTrace stackTrace) {
if (!_doneCompleter.isCompleted) {
_hasError = true;
_doneCompleter.completeError(error, stackTrace);
@ -693,7 +693,7 @@ class _StreamSinkImpl<T> implements StreamSink<T> {
// No new stream, .close was called. Close _target.
_closeTarget();
}
}, onError: (Object error, StackTrace? stackTrace) {
}, onError: (Object error, StackTrace stackTrace) {
if (_isBound) {
// A new stream takes over - forward errors to that stream.
_controllerCompleter!.completeError(error, stackTrace);
@ -1715,7 +1715,7 @@ class _HttpClientConnection {
if (incoming.statusCode == 100) {
incoming.drain().then((_) {
_subscription!.resume();
}).catchError((error, [StackTrace? stackTrace]) {
}).catchError((dynamic error, StackTrace stackTrace) {
_nextResponseCompleter!.completeError(
new HttpException(error.message, uri: _currentUri), stackTrace);
_nextResponseCompleter = null;
@ -1724,7 +1724,7 @@ class _HttpClientConnection {
_nextResponseCompleter!.complete(incoming);
_nextResponseCompleter = null;
}
}, onError: (error, [StackTrace? stackTrace]) {
}, onError: (dynamic error, StackTrace stackTrace) {
_nextResponseCompleter?.completeError(
new HttpException(error.message, uri: _currentUri), stackTrace);
_nextResponseCompleter = null;

View file

@ -901,7 +901,7 @@ class _WebSocketConsumer implements StreamConsumer {
socket.addStream(stream).then((_) {
_done();
_closeCompleter.complete(webSocket);
}, onError: (Object error, StackTrace? stackTrace) {
}, onError: (Object error, StackTrace stackTrace) {
_closed = true;
_cancel();
if (error is ArgumentError) {
@ -1144,7 +1144,7 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
} else {
_controller.add(data);
}
}, onError: (Object error, StackTrace? stackTrace) {
}, onError: (Object error, StackTrace stackTrace) {
_closeTimer?.cancel();
if (error is FormatException) {
_close(WebSocketStatus.invalidFramePayloadData);

View file

@ -196,7 +196,7 @@ class Timer {
}
@patch
void _rethrow(Object error, StackTrace? stackTrace) {
void _rethrow(Object error, StackTrace stackTrace) {
JS('', 'throw #', dart.createErrorWithStack(error, stackTrace));
}

View file

@ -203,7 +203,8 @@ class _AsyncAwaitCompleter<T> implements Completer<T> {
}
}
void completeError(e, [st]) {
void completeError(Object e, [StackTrace? st]) {
st ??= AsyncError.defaultStackTrace(e);
if (isSync) {
_future._completeError(e, st);
} else {

View file

@ -33,7 +33,8 @@ class _AsyncAwaitCompleter<T> implements Completer<T> {
}
}
void completeError(e, [st]) {
void completeError(Object e, [StackTrace? st]) {
st ??= AsyncError.defaultStackTrace(e);
if (isSync) {
_future._completeError(e, st);
} else {
@ -77,7 +78,7 @@ Function _asyncThenWrapperHelper(continuation) {
// parameter to the continuation. See vm/ast_transformer.cc for usage.
Function _asyncErrorWrapperHelper(continuation) {
// See comments of `_asyncThenWrapperHelper`.
var errorCallback = (e, s) => continuation(null, e, s);
void errorCallback(Object e, StackTrace s) => continuation(null, e, s);
if (Zone.current == Zone.root) return errorCallback;
return Zone.current.registerBinaryCallback(errorCallback);
}

View file

@ -5,7 +5,7 @@
part of dart.async;
_invokeErrorHandler(
Function errorHandler, Object error, StackTrace? stackTrace) {
Function errorHandler, Object error, StackTrace stackTrace) {
var handler = errorHandler; // Rename to avoid promotion.
if (handler is ZoneBinaryCallback<dynamic, Never, Never>) {
// Dynamic invocation because we don't know the actual type of the

View file

@ -23,8 +23,11 @@ class _BroadcastSubscription<T> extends _ControllerSubscription<T> {
_BroadcastSubscription<T>? _next;
_BroadcastSubscription<T>? _previous;
_BroadcastSubscription(_StreamControllerLifecycle<T> controller,
void onData(T data)?, Function? onError, void onDone()?,
_BroadcastSubscription(
_StreamControllerLifecycle<T> controller,
void onData(T data)?,
Function? onError,
void onDone()?,
bool cancelOnError)
: super(controller, onData, onError, onDone, cancelOnError) {
_next = _previous = this;
@ -255,7 +258,10 @@ abstract class _BroadcastStreamController<T>
if (replacement != null) {
error = replacement.error;
stackTrace = replacement.stackTrace;
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) throw "unreachable"; // TODO(40088)
_sendError(error, stackTrace);
}
@ -287,7 +293,7 @@ abstract class _BroadcastStreamController<T>
_sendData(data);
}
void _addError(Object error, StackTrace? stackTrace) {
void _addError(Object error, StackTrace stackTrace) {
_sendError(error, stackTrace);
}
@ -386,7 +392,7 @@ class _SyncBroadcastStreamController<T> extends _BroadcastStreamController<T>
});
}
void _sendError(Object error, StackTrace? stackTrace) {
void _sendError(Object error, StackTrace stackTrace) {
if (_isEmpty) return;
_forEachListener((_BufferingStreamSubscription<T> subscription) {
subscription._addError(error, stackTrace);
@ -419,7 +425,7 @@ class _AsyncBroadcastStreamController<T> extends _BroadcastStreamController<T> {
}
}
void _sendError(Object error, StackTrace? stackTrace) {
void _sendError(Object error, StackTrace stackTrace) {
for (var subscription = _firstSubscription;
subscription != null;
subscription = subscription._next) {
@ -480,6 +486,7 @@ class _AsBroadcastStreamController<T> extends _SyncBroadcastStreamController<T>
void addError(Object error, [StackTrace? stackTrace]) {
// TODO(40614): Remove once non-nullability is sound.
ArgumentError.checkNotNull(error, "error");
stackTrace ??= AsyncError.defaultStackTrace(error);
if (!isClosed && _isFiring) {
_addPendingEvent(new _DelayedError(error, stackTrace));
return;

View file

@ -282,6 +282,7 @@ abstract class Future<T> {
stackTrace = replacement.stackTrace;
}
}
stackTrace ??= AsyncError.defaultStackTrace(error);
return new _Future<T>.immediateError(error, stackTrace);
}
@ -364,13 +365,13 @@ abstract class Future<T> {
final _Future<List<T>> result = new _Future<List<T>>();
List<T?>? values; // Collects the values. Set to null on error.
int remaining = 0; // How many futures are we waiting for.
Object? error; // The first error from a future.
StackTrace? stackTrace; // The stackTrace that came with the error.
late Object error; // The first error from a future.
late StackTrace stackTrace; // The stackTrace that came with the error.
// Handle an error from any of the futures.
// TODO(jmesserly): use `void` return type once it can be inferred for the
// `then` call below.
handleError(theError, StackTrace? theStackTrace) {
handleError(Object theError, StackTrace theStackTrace) {
remaining--;
List<T?>? valueList = values;
if (valueList != null) {
@ -393,7 +394,7 @@ abstract class Future<T> {
stackTrace = theStackTrace;
}
} else if (remaining == 0 && !eagerError) {
result._completeError(error!, stackTrace);
result._completeError(error, stackTrace);
}
}
@ -411,7 +412,7 @@ abstract class Future<T> {
result._completeWithValue(List<T>.from(valueList));
}
} else {
// An error has occurred earlier.
// Forced read of error to assert that it has occurred earlier.
assert(error != null);
if (cleanUp != null && value != null) {
// Ensure errors from cleanUp are uncaught.
@ -420,7 +421,7 @@ abstract class Future<T> {
});
}
if (remaining == 0 && !eagerError) {
result._completeError(error!, stackTrace);
result._completeError(error, stackTrace);
}
}
}, onError: handleError);
@ -470,12 +471,14 @@ abstract class Future<T> {
*/
static Future<T> any<T>(Iterable<Future<T>> futures) {
var completer = new Completer<T>.sync();
var onValue = (T value) {
void onValue(T value) {
if (!completer.isCompleted) completer.complete(value);
};
var onError = (Object error, StackTrace? stack) {
}
void onError(Object error, StackTrace stack) {
if (!completer.isCompleted) completer.completeError(error, stack);
};
}
for (var future in futures) {
future.then(onValue, onError: onError);
}
@ -649,7 +652,7 @@ abstract class Future<T> {
*/
// The `Function` below stands for one of two types:
// - (dynamic) -> FutureOr<T>
// - (dynamic, StackTrace?) -> FutureOr<T>
// - (dynamic, StackTrace) -> FutureOr<T>
// Given that there is a `test` function that is usually used to do an
// `isCheck` we should also expect functions that take a specific argument.
Future<T> catchError(Function onError, {bool test(Object error)?});
@ -919,14 +922,17 @@ abstract class Completer<T> {
}
// Helper function completing a _Future with error, but checking the zone
// for error replacement first.
// for error replacement and missing stack trace first.
void _completeWithErrorCallback(
_Future result, Object error, StackTrace? stackTrace) {
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
stackTrace = replacement.stackTrace;
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) throw "unreachable"; // TODO(40088).
result._completeError(error, stackTrace);
}
@ -937,6 +943,11 @@ void _asyncCompleteWithErrorCallback(
if (replacement != null) {
error = replacement.error;
stackTrace = replacement.stackTrace;
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) {
throw "unreachable"; // TODO(lrn): Remove when type promotion works.
}
result._asyncCompleteError(error, stackTrace);
}

View file

@ -24,11 +24,14 @@ abstract class _Completer<T> implements Completer<T> {
if (replacement != null) {
error = replacement.error;
stackTrace = replacement.stackTrace;
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) throw "unreachable"; // TODO(40088)
_completeError(error, stackTrace);
}
void _completeError(Object error, StackTrace? stackTrace);
void _completeError(Object error, StackTrace stackTrace);
// The future's _isComplete doesn't take into account pending completions.
// We therefore use _mayComplete.
@ -41,7 +44,7 @@ class _AsyncCompleter<T> extends _Completer<T> {
future._asyncComplete(value as FutureOr<T>);
}
void _completeError(Object error, StackTrace? stackTrace) {
void _completeError(Object error, StackTrace stackTrace) {
future._asyncCompleteError(error, stackTrace);
}
}
@ -52,7 +55,7 @@ class _SyncCompleter<T> extends _Completer<T> {
future._complete(value as FutureOr<T>);
}
void _completeError(Object error, StackTrace? stackTrace) {
void _completeError(Object error, StackTrace stackTrace) {
future._completeError(error, stackTrace);
}
}
@ -150,8 +153,8 @@ class _FutureListener<S, T> {
var errorCallback = this.errorCallback; // To enable promotion.
// If the errorCallback returns something which is not a FutureOr<T>,
// this return statement throws, and the caller handles the error.
if (errorCallback is dynamic Function(Object, StackTrace?)) {
return _zone.runBinary<dynamic, Object, StackTrace?>(
if (errorCallback is dynamic Function(Object, StackTrace)) {
return _zone.runBinary<dynamic, Object, StackTrace>(
errorCallback, asyncError.error, asyncError.stackTrace);
} else {
return _zone.runUnary<dynamic, Object>(
@ -229,7 +232,7 @@ class _Future<T> implements Future<T> {
_setValue(value);
}
_Future.immediateError(var error, [StackTrace? stackTrace])
_Future.immediateError(var error, StackTrace stackTrace)
: _zone = Zone._current {
_asyncCompleteError(error, stackTrace);
}
@ -353,7 +356,7 @@ class _Future<T> implements Future<T> {
_resultOrListeners = error;
}
void _setError(Object error, StackTrace? stackTrace) {
void _setError(Object error, StackTrace stackTrace) {
_setErrorObject(new AsyncError(error, stackTrace));
}
@ -465,12 +468,7 @@ class _Future<T> implements Future<T> {
// so use _complete instead of _completeWithValue.
target._clearPendingComplete(); // Clear this first, it's set again.
target._complete(value);
},
// TODO(floitsch): eventually we would like to make this non-optional
// and dependent on the listeners of the target future. If none of
// the target future's listeners want to have the stack trace we don't
// need a trace.
onError: (Object error, [StackTrace? stackTrace]) {
}, onError: (Object error, StackTrace stackTrace) {
assert(target._isPendingComplete);
target._completeError(error, stackTrace);
});
@ -528,7 +526,7 @@ class _Future<T> implements Future<T> {
_propagateToListeners(this, listeners);
}
void _completeError(Object error, [StackTrace? stackTrace]) {
void _completeError(Object error, StackTrace stackTrace) {
assert(!_isComplete);
_FutureListener? listeners = _removeListeners();
@ -577,7 +575,7 @@ class _Future<T> implements Future<T> {
_chainForeignFuture(value, this);
}
void _asyncCompleteError(Object error, StackTrace? stackTrace) {
void _asyncCompleteError(Object error, StackTrace stackTrace) {
assert(!_isComplete);
_setPendingComplete();
@ -767,7 +765,8 @@ class _Future<T> implements Future<T> {
if (onTimeout == null) {
timer = new Timer(timeLimit, () {
result._completeError(
new TimeoutException("Future not completed", timeLimit));
new TimeoutException("Future not completed", timeLimit),
StackTrace.empty);
});
} else {
Zone zone = Zone.current;
@ -787,7 +786,7 @@ class _Future<T> implements Future<T> {
timer.cancel();
result._completeWithValue(v);
}
}, onError: (Object e, StackTrace? s) {
}, onError: (Object e, StackTrace s) {
if (timer.isActive) {
timer.cancel();
result._completeError(e, s);

View file

@ -154,7 +154,7 @@ abstract class Stream<T> {
// TODO(40614): Remove once non-nullability is sound.
ArgumentError.checkNotNull(error, "error");
return (_AsyncStreamController<T>(null, null, null, null)
.._addError(error, stackTrace)
.._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error))
.._closeUnchecked())
.stream;
}
@ -203,18 +203,20 @@ abstract class Stream<T> {
// Declare these as variables holding closures instead of as
// function declarations.
// This avoids creating a new closure from the functions for each future.
var onValue = (T value) {
void onValue(T value) {
if (!controller.isClosed) {
controller._add(value);
if (--count == 0) controller._closeUnchecked();
}
};
var onError = (Object error, StackTrace? stack) {
}
void onError(Object error, StackTrace stack) {
if (!controller.isClosed) {
controller._addError(error, stack);
if (--count == 0) controller._closeUnchecked();
}
};
}
// The futures are already running, so start listening to them immediately
// (instead of waiting for the stream to be listened on).
// If we wait, we might not catch errors in the futures in time.
@ -411,7 +413,7 @@ abstract class Stream<T> {
* error object and possibly a stack trace.
*
* The [onError] callback must be of type `void onError(Object error)` or
* `void onError(Object error, StackTrace? stackTrace)`. If [onError] accepts
* `void onError(Object error, StackTrace stackTrace)`. If [onError] accepts
* two arguments it is called with the error object and the stack trace
* (which could be `null` if this stream itself received an error without
* stack trace).
@ -508,6 +510,7 @@ abstract class Stream<T> {
}
final addError = controller._addError;
final resume = subscription.resume;
subscription.onData((T event) {
FutureOr<E> newValue;
try {
@ -518,9 +521,7 @@ abstract class Stream<T> {
}
if (newValue is Future<E>) {
subscription.pause();
newValue
.then(add, onError: addError)
.whenComplete(subscription.resume);
newValue.then(add, onError: addError).whenComplete(resume);
} else {
if (newValue is! E) {
throw "unreachable"; // TODO(lrn): Remove when type promotion works.
@ -532,7 +533,7 @@ abstract class Stream<T> {
if (!isBroadcast) {
controller
..onPause = subscription.pause
..onResume = subscription.resume;
..onResume = resume;
}
};
return controller.stream;
@ -598,7 +599,7 @@ abstract class Stream<T> {
* by the [onError] function.
*
* The [onError] callback must be of type `void onError(Object error)` or
* `void onError(Object error, StackTrace? stackTrace)`.
* `void onError(Object error, StackTrace stackTrace)`.
* The function type determines whether [onError] is invoked with a stack
* trace argument.
* The stack trace argument may be `null` if this stream received an error
@ -1471,7 +1472,8 @@ abstract class Stream<T> {
subscription =
this.listen(null, onError: result._completeError, onDone: () {
result._completeError(
new RangeError.index(index, this, "index", null, elementIndex));
new RangeError.index(index, this, "index", null, elementIndex),
StackTrace.empty);
}, cancelOnError: true);
subscription.onData((T value) {
if (index == elementIndex) {
@ -1555,7 +1557,7 @@ abstract class Stream<T> {
// issue: https://github.com/dart-lang/sdk/issues/37565
controller.add(event);
})
..onError((Object error, StackTrace? stackTrace) {
..onError((Object error, StackTrace stackTrace) {
timer.cancel();
timer = zone.createTimer(timeLimit, timeoutCallback);
controller._addError(
@ -2048,8 +2050,7 @@ abstract class StreamTransformer<S, T> {
*/
factory StreamTransformer.fromHandlers(
{void handleData(S data, EventSink<T> sink)?,
void handleError(
Object error, StackTrace? stackTrace, EventSink<T> sink)?,
void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?,
void handleDone(EventSink<T> sink)?}) = _StreamHandlerTransformer<S, T>;
/**

View file

@ -607,7 +607,10 @@ abstract class _StreamController<T> implements _StreamControllerBase<T> {
if (replacement != null) {
error = replacement.error;
stackTrace = replacement.stackTrace;
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) throw "unreachable"; // TODO(40088)
_addError(error, stackTrace);
}
@ -654,7 +657,7 @@ abstract class _StreamController<T> implements _StreamControllerBase<T> {
}
}
void _addError(Object error, StackTrace? stackTrace) {
void _addError(Object error, StackTrace stackTrace) {
if (hasListener) {
_sendError(error, stackTrace);
} else if (_isInitialState) {
@ -780,7 +783,7 @@ abstract class _SyncStreamControllerDispatch<T>
_subscription._add(data);
}
void _sendError(Object error, StackTrace? stackTrace) {
void _sendError(Object error, StackTrace stackTrace) {
_subscription._addError(error, stackTrace);
}
@ -795,7 +798,7 @@ abstract class _AsyncStreamControllerDispatch<T>
_subscription._addPending(_DelayedData<T>(data));
}
void _sendError(Object error, StackTrace? stackTrace) {
void _sendError(Object error, StackTrace stackTrace) {
_subscription._addPending(_DelayedError(error, stackTrace));
}
@ -903,7 +906,7 @@ class _AddStreamState<T> {
onDone: controller._close,
cancelOnError: cancelOnError);
static makeErrorHandler(_EventSink controller) => (Object e, StackTrace? s) {
static makeErrorHandler(_EventSink controller) => (Object e, StackTrace s) {
controller._addError(e, s);
controller._close();
};

View file

@ -7,7 +7,7 @@ part of dart.async;
/** Abstract and private interface for a place to put events. */
abstract class _EventSink<T> {
void _add(T data);
void _addError(Object error, StackTrace? stackTrace);
void _addError(Object error, StackTrace stackTrace);
void _close();
}
@ -20,7 +20,7 @@ abstract class _EventSink<T> {
*/
abstract class _EventDispatch<T> {
void _sendData(T data);
void _sendError(Object error, StackTrace? stackTrace);
void _sendError(Object error, StackTrace stackTrace);
void _sendDone();
}
@ -153,9 +153,9 @@ class _BufferingStreamSubscription<T>
static Function _registerErrorHandler(Zone zone, Function? handleError) {
// TODO(lrn): Consider whether we need to register the null handler.
handleError ??= _nullErrorHandler;
if (handleError is void Function(Object, StackTrace?)) {
if (handleError is void Function(Object, StackTrace)) {
return zone
.registerBinaryCallback<dynamic, Object, StackTrace?>(handleError);
.registerBinaryCallback<dynamic, Object, StackTrace>(handleError);
}
if (handleError is void Function(Object)) {
return zone.registerUnaryCallback<dynamic, Object>(handleError);
@ -220,7 +220,7 @@ class _BufferingStreamSubscription<T>
_onDone = () {
result._complete(resultValue);
};
_onError = (Object error, StackTrace? stackTrace) {
_onError = (Object error, StackTrace stackTrace) {
Future cancelFuture = cancel();
if (!identical(cancelFuture, Future._nullFuture)) {
cancelFuture.whenComplete(() {
@ -283,7 +283,7 @@ class _BufferingStreamSubscription<T>
}
}
void _addError(Object error, StackTrace? stackTrace) {
void _addError(Object error, StackTrace stackTrace) {
if (_isCanceled) return;
if (_canFire) {
_sendError(error, stackTrace); // Reports cancel after sending.
@ -352,7 +352,7 @@ class _BufferingStreamSubscription<T>
_checkState(wasInputPaused);
}
void _sendError(Object error, StackTrace? stackTrace) {
void _sendError(Object error, StackTrace stackTrace) {
assert(!_isCanceled);
assert(!_isPaused);
assert(!_inCallback);
@ -365,8 +365,8 @@ class _BufferingStreamSubscription<T>
_state |= _STATE_IN_CALLBACK;
// TODO(floitsch): this dynamic should be 'void'.
var onError = _onError;
if (onError is void Function(Object, StackTrace?)) {
_zone.runBinaryGuarded<Object, StackTrace?>(onError, error, stackTrace);
if (onError is void Function(Object, StackTrace)) {
_zone.runBinaryGuarded<Object, StackTrace>(onError, error, stackTrace);
} else {
_zone.runUnaryGuarded<Object>(_onError as void Function(Object), error);
}
@ -581,7 +581,7 @@ typedef void _DoneHandler();
void _nullDataHandler(dynamic value) {}
/** Default error handler, reports the error to the current zone's handler. */
void _nullErrorHandler(Object error, [StackTrace? stackTrace]) {
void _nullErrorHandler(Object error, StackTrace stackTrace) {
Zone.current.handleUncaughtError(error, stackTrace);
}
@ -608,7 +608,7 @@ class _DelayedData<T> extends _DelayedEvent<T> {
/** A delayed error event. */
class _DelayedError extends _DelayedEvent {
final Object error;
final StackTrace? stackTrace;
final StackTrace stackTrace;
_DelayedError(this.error, this.stackTrace);
void perform(_EventDispatch dispatch) {
@ -990,8 +990,7 @@ class _StreamIterator<T> implements StreamIterator<T> {
/// completed.
bool _isPaused = false;
_StreamIterator(final Stream<T> stream)
: _stateData = stream {
_StreamIterator(final Stream<T> stream) : _stateData = stream {
ArgumentError.checkNotNull(stream, "stream");
}
@ -1060,7 +1059,7 @@ class _StreamIterator<T> implements StreamIterator<T> {
if (_isPaused) _subscription?.pause();
}
void _onError(Object error, [StackTrace? stackTrace]) {
void _onError(Object error, StackTrace stackTrace) {
assert(_subscription != null && !_isPaused);
var moveNextFuture = _stateData as _Future<bool>;
_subscription = null;

View file

@ -5,8 +5,8 @@
part of dart.async;
/** Runs user code and takes actions depending on success or failure. */
_runUserCode<T>(
T userCode(), onSuccess(T value), onError(Object error, StackTrace? stackTrace)) {
_runUserCode<T>(T userCode(), onSuccess(T value),
onError(Object error, StackTrace stackTrace)) {
try {
onSuccess(userCode());
} catch (e, s) {
@ -24,7 +24,7 @@ _runUserCode<T>(
/** Helper function to cancel a subscription and wait for the potential future,
before completing with an error. */
void _cancelAndError(StreamSubscription subscription, _Future future,
Object error, StackTrace? stackTrace) {
Object error, StackTrace stackTrace) {
var cancelFuture = subscription.cancel();
if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) {
cancelFuture.whenComplete(() => future._completeError(error, stackTrace));
@ -34,7 +34,7 @@ void _cancelAndError(StreamSubscription subscription, _Future future,
}
void _cancelAndErrorWithReplacement(StreamSubscription subscription,
_Future future, Object error, StackTrace? stackTrace) {
_Future future, Object error, StackTrace stackTrace) {
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
@ -43,12 +43,10 @@ void _cancelAndErrorWithReplacement(StreamSubscription subscription,
_cancelAndError(subscription, future, error, stackTrace);
}
typedef void _ErrorCallback(Object error, StackTrace? stackTrace);
/** Helper function to make an onError argument to [_runUserCode]. */
_ErrorCallback _cancelAndErrorClosure(
void Function(Object error, StackTrace stackTrace) _cancelAndErrorClosure(
StreamSubscription subscription, _Future future) {
return (Object error, StackTrace? stackTrace) {
return (Object error, StackTrace stackTrace) {
_cancelAndError(subscription, future, error, stackTrace);
};
}
@ -95,7 +93,7 @@ abstract class _ForwardingStream<S, T> extends Stream<T> {
void _handleData(S data, _EventSink<T> sink);
void _handleError(Object error, StackTrace? stackTrace, _EventSink<T> sink) {
void _handleError(Object error, StackTrace stackTrace, _EventSink<T> sink) {
sink._addError(error, stackTrace);
}
@ -129,7 +127,7 @@ class _ForwardingStreamSubscription<S, T>
super._add(data);
}
void _addError(Object error, StackTrace? stackTrace) {
void _addError(Object error, StackTrace stackTrace) {
if (_isClosed) return;
super._addError(error, stackTrace);
}
@ -159,7 +157,7 @@ class _ForwardingStreamSubscription<S, T>
_stream._handleData(data, this);
}
void _handleError(error, StackTrace? stackTrace) {
void _handleError(error, StackTrace stackTrace) {
_stream._handleError(error, stackTrace, this);
}
@ -173,7 +171,7 @@ class _ForwardingStreamSubscription<S, T>
// -------------------------------------------------------------------
void _addErrorWithReplacement(
_EventSink sink, Object error, StackTrace? stackTrace) {
_EventSink sink, Object error, StackTrace stackTrace) {
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
@ -268,7 +266,7 @@ class _HandleErrorStream<T> extends _ForwardingStream<T, T> {
sink._add(data);
}
void _handleError(Object error, StackTrace? stackTrace, _EventSink<T> sink) {
void _handleError(Object error, StackTrace stackTrace, _EventSink<T> sink) {
bool matches = true;
var test = _test;
if (test != null) {

View file

@ -16,7 +16,7 @@ class _EventSinkWrapper<T> implements EventSink<T> {
}
void addError(Object error, [StackTrace? stackTrace]) {
_sink._addError(error, stackTrace);
_sink._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error));
}
void close() {
@ -76,7 +76,7 @@ class _SinkTransformerStreamSubscription<S, T>
* events when the stream is already closed. Report them as state
* error.
*/
void _addError(Object error, StackTrace? stackTrace) {
void _addError(Object error, StackTrace stackTrace) {
if (_isClosed) {
throw new StateError("Stream is already closed");
}
@ -124,7 +124,7 @@ class _SinkTransformerStreamSubscription<S, T>
}
}
void _handleError(Object error, StackTrace? stackTrace) {
void _handleError(Object error, StackTrace stackTrace) {
try {
_transformerSink.addError(error, stackTrace);
} catch (e, s) {
@ -193,7 +193,7 @@ typedef void _TransformDataHandler<S, T>(S data, EventSink<T> sink);
/// Error-handler coming from [StreamTransformer.fromHandlers].
typedef void _TransformErrorHandler<T>(
Object error, StackTrace? stackTrace, EventSink<T> sink);
Object error, StackTrace stackTrace, EventSink<T> sink);
/// Done-handler coming from [StreamTransformer.fromHandlers].
typedef void _TransformDoneHandler<T>(EventSink<T> sink);
@ -236,6 +236,7 @@ class _HandlerEventSink<S, T> implements EventSink<S> {
throw StateError("Sink is closed");
}
var handleError = _handleError;
stackTrace ??= AsyncError.defaultStackTrace(error);
if (handleError != null) {
handleError(error, stackTrace, sink);
} else {
@ -264,8 +265,7 @@ class _HandlerEventSink<S, T> implements EventSink<S> {
class _StreamHandlerTransformer<S, T> extends _StreamSinkTransformer<S, T> {
_StreamHandlerTransformer(
{void handleData(S data, EventSink<T> sink)?,
void handleError(
Object error, StackTrace? stackTrace, EventSink<T> sink)?,
void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?,
void handleDone(EventSink<T> sink)?})
: super((EventSink<T> outputSink) {
return new _HandlerEventSink<S, T>(

View file

@ -9,7 +9,7 @@ typedef R ZoneUnaryCallback<R, T>(T arg);
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
typedef HandleUncaughtErrorHandler = void Function(Zone self,
ZoneDelegate parent, Zone zone, Object error, StackTrace? stackTrace);
ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace);
typedef RunHandler = R Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
typedef RunUnaryHandler = R Function<R, T>(
@ -39,13 +39,27 @@ typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
class AsyncError implements Error {
final Object error;
final StackTrace? stackTrace;
final StackTrace stackTrace;
AsyncError(this.error, this.stackTrace) {
AsyncError(this.error, StackTrace? stackTrace)
: stackTrace = stackTrace ?? defaultStackTrace(error) {
// TODO(40614): Remove once non-nullability is sound.
ArgumentError.checkNotNull(error, "error");
}
/// A default stack trace for an error.
///
/// If [error] is an [Error] and it has an [Error.stackTrace],
/// that stack trace is returned.
/// If not, the [StackTrace.empty] default stack trace is returned.
static StackTrace defaultStackTrace(Object error) {
if (error is Error) {
var stackTrace = error.stackTrace;
if (stackTrace != null) return stackTrace;
}
return StackTrace.empty;
}
String toString() => '$error';
}
@ -205,7 +219,7 @@ class _ZoneSpecification implements ZoneSpecification {
* to skip zones that would just delegate to their parents.
*/
abstract class ZoneDelegate {
void handleUncaughtError(Zone zone, Object error, StackTrace? stackTrace);
void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace);
R run<R>(Zone zone, R f());
R runUnary<R, T>(Zone zone, R f(T arg), T arg);
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2);
@ -312,7 +326,7 @@ abstract class Zone {
* By default, when handled by the root zone, uncaught asynchronous errors are
* treated like uncaught synchronous exceptions.
*/
void handleUncaughtError(Object error, StackTrace? stackTrace);
void handleUncaughtError(Object error, StackTrace stackTrace);
/**
* The parent zone of the this zone.
@ -694,7 +708,7 @@ class _ZoneDelegate implements ZoneDelegate {
_ZoneDelegate(this._delegationTarget);
void handleUncaughtError(Zone zone, Object error, StackTrace? stackTrace) {
void handleUncaughtError(Zone zone, Object error, StackTrace stackTrace) {
var implementation = _delegationTarget._handleUncaughtError;
_Zone implZone = implementation.zone;
HandleUncaughtErrorHandler handler = implementation.function;
@ -1019,7 +1033,7 @@ class _CustomZone extends _Zone {
// Methods that can be customized by the zone specification.
void handleUncaughtError(Object error, StackTrace? stackTrace) {
void handleUncaughtError(Object error, StackTrace stackTrace) {
var implementation = this._handleUncaughtError;
ZoneDelegate parentDelegate = implementation.zone._parentDelegate;
HandleUncaughtErrorHandler handler = implementation.function;
@ -1120,14 +1134,13 @@ class _CustomZone extends _Zone {
}
void _rootHandleUncaughtError(Zone? self, ZoneDelegate? parent, Zone zone,
Object error, StackTrace? stackTrace) {
Object error, StackTrace stackTrace) {
_schedulePriorityAsyncCallback(() {
if (stackTrace == null) throw error;
_rethrow(error, stackTrace);
});
}
external void _rethrow(Object error, StackTrace? 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();
@ -1383,7 +1396,7 @@ class _RootZone extends _Zone {
// Methods that can be customized by the zone specification.
void handleUncaughtError(Object error, StackTrace? stackTrace) {
void handleUncaughtError(Object error, StackTrace stackTrace) {
_rootHandleUncaughtError(null, null, this, error, stackTrace);
}
@ -1487,8 +1500,8 @@ R runZoned<R>(R body(),
return _runZoned<R>(body, zoneValues, zoneSpecification);
}
void Function(Object)? unaryOnError;
void Function(Object, StackTrace?)? binaryOnError;
if (onError is void Function(Object, StackTrace?)) {
void Function(Object, StackTrace)? binaryOnError;
if (onError is void Function(Object, StackTrace)) {
binaryOnError = onError;
} else if (onError is void Function(Object)) {
unaryOnError = onError;
@ -1498,7 +1511,7 @@ R runZoned<R>(R body(),
}
_Zone parentZone = Zone._current;
HandleUncaughtErrorHandler errorHandler = (Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace? stackTrace) {
Zone zone, Object error, StackTrace stackTrace) {
try {
if (binaryOnError != null) {
parentZone.runBinary(binaryOnError, error, stackTrace);

View file

@ -14,6 +14,13 @@ part of dart.core;
* them programmatically.
*/
abstract class StackTrace {
/// A stack trace object with no information.
///
/// This stack trace is used as the default in situations where
/// a stack trace is required, but the user has not supplied one.
@Since("2.8")
static const empty = const _StringStackTrace("");
StackTrace(); // In case existing classes extend StackTrace.
/**
@ -55,6 +62,6 @@ abstract class StackTrace {
class _StringStackTrace implements StackTrace {
final String _stackTrace;
_StringStackTrace(this._stackTrace);
const _StringStackTrace(this._stackTrace);
String toString() => _stackTrace;
}

View file

@ -50,9 +50,9 @@ class CastStreamSubscription<S, T> implements StreamSubscription<T> {
_source.onError(handleError);
if (handleError == null) {
_handleError = null;
} else if (handleError is void Function(Object, StackTrace?)) {
} else if (handleError is void Function(Object, StackTrace)) {
_handleError = _zone
.registerBinaryCallback<dynamic, Object, StackTrace?>(handleError);
.registerBinaryCallback<dynamic, Object, StackTrace>(handleError);
} else if (handleError is void Function(Object)) {
_handleError = _zone.registerUnaryCallback<dynamic, Object>(handleError);
} else {
@ -74,8 +74,8 @@ class CastStreamSubscription<S, T> implements StreamSubscription<T> {
var handleError = _handleError;
if (handleError == null) {
_zone.handleUncaughtError(error, stack);
} else if (handleError is void Function(Object, StackTrace?)) {
_zone.runBinaryGuarded<Object, StackTrace?>(handleError, error, stack);
} else if (handleError is void Function(Object, StackTrace)) {
_zone.runBinaryGuarded<Object, StackTrace>(handleError, error, stack);
} else {
_zone.runUnaryGuarded<Object>(
handleError as void Function(Object), error);

View file

@ -2,14 +2,14 @@
// 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.
/// Regression test: stack trace can be null when using async-await.
/// Regression test: stack trace could be null when using async-await.
import 'dart:async';
import 'package:expect/expect.dart';
main() async {
C value = await test();
Expect.equals("[[null]]", "$value");
Expect.identical(StackTrace.empty, value._s);
}
Future<C> test() async {
@ -17,7 +17,7 @@ Future<C> test() async {
await throwInFuture();
return C(StackTrace.fromString("no-throw"));
} on MyException catch (e, s) {
return C(s); // Note: s is null
return C(s); // Note: s is *no longer* null
}
}

View file

@ -2248,7 +2248,8 @@ class FakeValueFuture implements Future {
Future timeout(Duration duration, {onTimeout()?}) => this;
}
typedef BinaryFunction(a, b);
typedef OnErrorCallback2 = dynamic Function(Object, StackTrace);
typedef OnErrorCallback1 = dynamic Function(Object);
/**
* A non-standard implementation of Future with an error.
@ -2258,10 +2259,17 @@ class FakeErrorFuture implements Future {
FakeErrorFuture(this._error);
Future<S> then<S>(callback(value), {Function? onError}) {
if (onError != null) {
if (onError is BinaryFunction) {
return new Future<S>.microtask(() => onError(_error, null));
if (onError is OnErrorCallback2) {
return new Future<S>.microtask(() => onError(_error, StackTrace.empty));
} else if (onError is OnErrorCallback1) {
return new Future<S>.microtask(() => onError(_error));
} else {
throw new ArgumentError.value(
onError,
"onError",
"Error handler must accept one Object or one Object and a StackTrace"
" as arguments, and return a valid result");
}
return new Future<S>.microtask(() => onError(_error));
}
return new Future<S>.error(_error);
}
@ -2275,10 +2283,17 @@ class FakeErrorFuture implements Future {
Future catchError(Function onError, {bool test(Object error)?}) {
return new Future.microtask(() {
if (test != null && !test(_error)) return this;
if (onError is BinaryFunction) {
return onError(_error, null);
if (onError is OnErrorCallback2) {
return onError(_error, StackTrace.empty);
} else if (onError is OnErrorCallback1) {
return onError(_error);
} else {
throw new ArgumentError.value(
onError,
"onError",
"Error handler must accept one Object or one Object and a StackTrace"
" as arguments, and return a valid result");
}
return onError(_error);
});
}

View file

@ -2247,7 +2247,8 @@ class FakeValueFuture implements Future {
Future timeout(Duration duration, {onTimeout()}) => this;
}
typedef BinaryFunction(a, b);
typedef OnErrorCallback2 = dynamic Function(Object, StackTrace);
typedef OnErrorCallback1 = dynamic Function(Object);
/**
* A non-standard implementation of Future with an error.
@ -2257,10 +2258,17 @@ class FakeErrorFuture implements Future {
FakeErrorFuture(this._error);
Future<S> then<S>(callback(value), {Function onError}) {
if (onError != null) {
if (onError is BinaryFunction) {
if (onError is OnErrorCallback2) {
return new Future<S>.microtask(() => onError(_error, null));
} else if (onError is OnErrorCallback1) {
return new Future<S>.microtask(() => onError(_error));
} else {
throw new ArgumentError.value(
onError,
"onError",
"Error handler must accept one Object or one Object and a StackTrace"
" as arguments, and return a valid result");
}
return new Future<S>.microtask(() => onError(_error));
}
return new Future<S>.error(_error);
}
@ -2274,10 +2282,17 @@ class FakeErrorFuture implements Future {
Future catchError(Function onError, {bool test(error)}) {
return new Future.microtask(() {
if (test != null && !test(_error)) return this;
if (onError is BinaryFunction) {
if (onError is OnErrorCallback2) {
return onError(_error, null);
} else if (onError is OnErrorCallback1) {
return onError(_error);
} else {
throw new ArgumentError.value(
onError,
"onError",
"Error handler must accept one Object or one Object and a StackTrace"
" as arguments, and return a valid result");
}
return onError(_error);
});
}

View file

@ -1,20 +0,0 @@
// Copyright (c) 2017, 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.
// Test source positions in async errors.
import "package:expect/expect.dart";
import "dart:io";
main() async {
try {
await Socket.connect("localhost", 0);
Expect.isTrue(false); // Unreachable.
} catch (e, s) {
Expect.isTrue(e is SocketException);
Expect.isTrue(s.toString().contains("regress_28325_test.dart"));
print(s);
Expect.isTrue(s.toString().contains(":12")); // Line number of "await".
}
}

View file

@ -56,7 +56,7 @@ Future testWaitWithSingleError() {
throw 'incorrect error';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -73,7 +73,7 @@ Future testWaitWithMultipleErrors() {
throw 'incorrect error 2';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -90,7 +90,7 @@ Future testWaitWithMultipleErrorsEager() {
throw 'incorrect error 2';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -192,8 +192,8 @@ Future testForEach() {
Future testForEachSync() {
final seen = <int>[];
return Future.forEach([1, 2, 3, 4, 5], seen.add).then(
(_) => Expect.listEquals([1, 2, 3, 4, 5], seen));
return Future.forEach([1, 2, 3, 4, 5], seen.add)
.then((_) => Expect.listEquals([1, 2, 3, 4, 5], seen));
}
Future testForEachWithException() {

View file

@ -16,7 +16,7 @@ main() {
expect(tos.isBroadcast, false);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -77,7 +77,7 @@ main() {
expect(tos.isBroadcast, true);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -89,7 +89,7 @@ main() {
expect(tos.isBroadcast, true);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -101,7 +101,7 @@ main() {
expect(tos.isBroadcast, false);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});

View file

@ -56,7 +56,7 @@ Future testWaitWithSingleError() {
throw 'incorrect error';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -73,7 +73,7 @@ Future testWaitWithMultipleErrors() {
throw 'incorrect error 2';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -90,7 +90,7 @@ Future testWaitWithMultipleErrorsEager() {
throw 'incorrect error 2';
}).catchError((error, stackTrace) {
Expect.equals('correct error', error);
Expect.isNull(stackTrace);
Expect.isNotNull(stackTrace);
});
}
@ -100,7 +100,7 @@ StackTrace get currentStackTrace {
} catch (e, st) {
return st;
}
return null;
throw "unreachable";
}
Future testWaitWithSingleErrorWithStackTrace() {
@ -193,8 +193,8 @@ Future testForEach() {
Future testForEachSync() {
final seen = <int>[];
return Future.forEach([1, 2, 3, 4, 5], seen.add).then(
(_) => Expect.listEquals([1, 2, 3, 4, 5], seen));
return Future.forEach([1, 2, 3, 4, 5], seen.add)
.then((_) => Expect.listEquals([1, 2, 3, 4, 5], seen));
}
Future testForEachWithException() {

View file

@ -16,7 +16,7 @@ main() {
expect(tos.isBroadcast, false);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -77,7 +77,7 @@ main() {
expect(tos.isBroadcast, true);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -89,7 +89,7 @@ main() {
expect(tos.isBroadcast, true);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});
@ -101,7 +101,7 @@ main() {
expect(tos.isBroadcast, false);
tos.handleError(expectAsync((e, s) {
expect(e, new isInstanceOf<TimeoutException>());
expect(s, null);
expect(s, StackTrace.empty);
})).listen((v) {
fail("Unexpected event");
});