Add missing null-tests to async error functions.

Also treat null errors comming out of Zone.errorCallback as
NullThrownError.

This should prevent, as was always the intention, any async error
from having a null value.
This is important for async/await syntax, where the distinction
between sync and async errors is removed.

(For Dart 2.0, we could just make null throwable).

R=sgjesse@google.com

Review URL: https://codereview.chromium.org//598993002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@40673 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
lrn@google.com
2014-09-25 11:00:49 +00:00
parent 8bba32d131
commit 181efd76ee
10 changed files with 43 additions and 24 deletions
@@ -238,10 +238,11 @@ abstract class _BroadcastStreamController<T>
}
void addError(Object error, [StackTrace stackTrace]) {
error = _nonNullError(error);
if (!_mayAddEvent) throw _addEventError();
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
_sendError(error, stackTrace);
+12 -6
View File
@@ -187,13 +187,16 @@ abstract class Future<T> {
/**
* A future that completes with an error in the next event-loop iteration.
*
* Use [Completer] to create a Future and complete it later.
* If [error] is `null`, it is replaced by a [NullThrownError].
*
* Use [Completer] to create a future and complete it later.
*/
factory Future.error(Object error, [StackTrace stackTrace]) {
error = _nonNullError(error);
if (!identical(Zone.current, _ROOT_ZONE)) {
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
}
@@ -663,10 +666,13 @@ abstract class Completer<T> {
// for error replacement first.
void _completeWithErrorCallback(_Future result, error, stackTrace) {
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement == null) {
result._completeError(error, stackTrace);
} else {
result._completeError(replacement.error, replacement.stackTrace);
if (replacement != null) {
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
result._completeError(error, stackTrace);
}
/** Helper function that converts `null` to a [NullThrownError]. */
Object _nonNullError(Object error) =>
(error != null) ? error : new NullThrownError();
+2 -3
View File
@@ -17,11 +17,11 @@ abstract class _Completer<T> implements Completer<T> {
void complete([value]);
void completeError(Object error, [StackTrace stackTrace]) {
if (error == null) throw new ArgumentError("Error must not be null");
error = _nonNullError(error);
if (!future._mayComplete) throw new StateError("Future already completed");
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
_completeError(error, stackTrace);
@@ -47,7 +47,6 @@ class _AsyncCompleter<T> extends _Completer<T> {
}
class _SyncCompleter<T> extends _Completer<T> {
void complete([value]) {
if (!future._mayComplete) throw new StateError("Future already completed");
future._complete(value);
+1 -1
View File
@@ -1382,7 +1382,7 @@ abstract class EventSink<T> implements Sink<T> {
void add(T event);
/** Send an async error to a stream. */
void addError(errorEvent, [StackTrace stackTrace]);
/** Send a done event to a stream.*/
/** Send a done event to a stream. */
void close();
}
+4 -1
View File
@@ -168,6 +168,8 @@ abstract class StreamController<T> implements StreamSink<T> {
/**
* Send or enqueue an error event.
*
* If [error] is `null`, it is replaced by a [NullThrownError].
*
* Also allows an objection stack trace object, on top of what [EventSink]
* allows.
*/
@@ -414,10 +416,11 @@ abstract class _StreamController<T> implements StreamController<T>,
* Send or enqueue an error event.
*/
void addError(Object error, [StackTrace stackTrace]) {
error = _nonNullError(error);
if (!_mayAddEvent) throw _badEventState();
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
_addError(error, stackTrace);
+8 -6
View File
@@ -15,7 +15,9 @@ _runUserCode(userCode(),
if (replacement == null) {
onError(e, s);
} else {
onError(replacement.error, replacement.stackTrace);
var error = _nonNullError(replacement.error);
var stackTrace = replacement.stackTrace;
onError(error, stackTrace);
}
}
}
@@ -39,7 +41,7 @@ void _cancelAndErrorWithReplacement(StreamSubscription subscription,
error, StackTrace stackTrace) {
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement != null) {
error = replacement.error;
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
_cancelAndError(subscription, future, error, stackTrace);
@@ -187,11 +189,11 @@ typedef bool _Predicate<T>(T value);
void _addErrorWithReplacement(_EventSink sink, error, stackTrace) {
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
if (replacement == null) {
sink._addError(error, stackTrace);
} else {
sink._addError(replacement.error, replacement.stackTrace);
if (replacement != null) {
error = _nonNullError(replacement.error);
stackTrace = replacement.stackTrace;
}
sink._addError(error, stackTrace);
}
+4 -3
View File
@@ -36,12 +36,13 @@ typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification,
Map zoneValues);
/// Pair of error and stack trace. Returned by [Zone.errorCallback].
/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
class AsyncError implements Error {
final error;
final StackTrace stackTrace;
AsyncError(this.error, this.stackTrace);
String toString() => error.toString();
}
@@ -254,10 +255,10 @@ abstract class Zone {
// Private constructor so that it is not possible instantiate a Zone class.
Zone._();
/// The root zone that is implicitly created.
/** The root zone that is implicitly created. */
static const Zone ROOT = _ROOT_ZONE;
/// The currently running zone.
/** The currently running zone. */
static Zone _current = _ROOT_ZONE;
static Zone get current => _current;
+3 -1
View File
@@ -17,7 +17,7 @@ LibTest/isolate/IsolateStream/contains_A02_t01: Fail # co19 issue 668
LibTest/typed_data/ByteData/buffer_A01_t01: Fail # co19 r736 bug - sent comment.
# TODO(terry) re-enable the below CSS tests when Chrome 38 and (Dartium 38) are ready issue 21075
LayoutTests/fast/css/getComputedStyle/computed-style-font_t01: Skip
LayoutTests/fast/css/getComputedStyle/computed-style-font_t01: Skip
LayoutTests/fast/css/font-shorthand-from-longhands_t01: Skip
Language/07_Classes/6_Constructors/1_Generative_Constructors_A01_t06: Fail, Pass, OK # co19 issue 695
@@ -29,6 +29,8 @@ WebPlatformTest/shadow-dom/elements-and-dom-objects/shadowroot-object/shadowroot
[ $compiler != dartanalyzer && $compiler != dart2analyzer ]
# Tests that fail on every runtime, but not on the analyzer.
LibTest/async/Future/Future.error_A01_t01: RuntimeError # co19 issue 712
LibTest/async/Completer/completeError_A02_t01: RuntimeError # co19 issue 712
LibTest/isolate/ReceivePort/asBroadcastStream_A02_t01: Fail # co19 issue 687
LibTest/async/Stream/asBroadcastStream_A02_t01: Fail # co19 issue 687
+6 -1
View File
@@ -677,8 +677,13 @@ void testCompleteErrorWithCustomFuture() {
}
void testCompleteErrorWithNull() {
asyncStart();
final completer = new Completer<int>();
Expect.throws(() => completer.completeError(null));
completer.future.catchError((e) {
Expect.isTrue(e is NullThrownError);
asyncEnd();
});
completer.completeError(null);
}
void testChainedFutureValue() {
@@ -380,7 +380,7 @@ testRethrow() {
Stream s = streamErrorTransform(c.stream, (e) { throw error; });
s.listen((_) { Expect.fail("unexpected value"); }, onError: expectAsync(
(e) { Expect.identical(error, e); }));
c.addError(null);
c.addError("SOME ERROR");
c.close();
});
}