Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: sdk/lib/async/future_impl.dart

Issue 555153002: Add error-intercept for Completer.completeError and StreamController.addError. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Intercept all errors thrown by unregistered callbacks. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/async/future.dart ('k') | sdk/lib/async/stream.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.async; 5 part of dart.async;
6 6
7 /** The onValue and onError handlers return either a value or a future */ 7 /** The onValue and onError handlers return either a value or a future */
8 typedef dynamic _FutureOnValue<T>(T value); 8 typedef dynamic _FutureOnValue<T>(T value);
9 /** Test used by [Future.catchError] to handle skip some errors. */ 9 /** Test used by [Future.catchError] to handle skip some errors. */
10 typedef bool _FutureErrorTest(var error); 10 typedef bool _FutureErrorTest(var error);
11 /** Used by [WhenFuture]. */ 11 /** Used by [WhenFuture]. */
12 typedef _FutureAction(); 12 typedef _FutureAction();
13 13
14 abstract class _Completer<T> implements Completer<T> { 14 abstract class _Completer<T> implements Completer<T> {
15 final _Future<T> future = new _Future<T>(); 15 final _Future<T> future = new _Future<T>();
16 16
17 void complete([value]); 17 void complete([value]);
18 18
19 void completeError(Object error, [StackTrace stackTrace]); 19 void completeError(Object error, [StackTrace stackTrace]) {
20 if (error == null) throw new ArgumentError("Error must not be null");
21 if (!future._mayComplete) throw new StateError("Future already completed");
22 AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
23 if (replacement != null) {
24 error = replacement.error;
25 stackTrace = replacement.stackTrace;
26 }
27 _completeError(error, stackTrace);
28 }
29
30 void _completeError(Object error, StackTrace stackTrace);
20 31
21 // The future's _isComplete doesn't take into account pending completions. 32 // The future's _isComplete doesn't take into account pending completions.
22 // We therefore use _mayComplete. 33 // We therefore use _mayComplete.
23 bool get isCompleted => !future._mayComplete; 34 bool get isCompleted => !future._mayComplete;
24 } 35 }
25 36
26 class _AsyncCompleter<T> extends _Completer<T> { 37 class _AsyncCompleter<T> extends _Completer<T> {
27 38
28 void complete([value]) { 39 void complete([value]) {
29 if (!future._mayComplete) throw new StateError("Future already completed"); 40 if (!future._mayComplete) throw new StateError("Future already completed");
30 future._asyncComplete(value); 41 future._asyncComplete(value);
31 } 42 }
32 43
33 void completeError(Object error, [StackTrace stackTrace]) { 44 void _completeError(Object error, StackTrace stackTrace) {
34 if (error == null) throw new ArgumentError("Error must not be null");
35 if (!future._mayComplete) throw new StateError("Future already completed");
36 future._asyncCompleteError(error, stackTrace); 45 future._asyncCompleteError(error, stackTrace);
37 } 46 }
38 } 47 }
39 48
40 class _SyncCompleter<T> extends _Completer<T> { 49 class _SyncCompleter<T> extends _Completer<T> {
41 50
42 void complete([value]) { 51 void complete([value]) {
43 if (!future._mayComplete) throw new StateError("Future already completed"); 52 if (!future._mayComplete) throw new StateError("Future already completed");
44 future._complete(value); 53 future._complete(value);
45 } 54 }
46 55
47 void completeError(Object error, [StackTrace stackTrace]) { 56 void _completeError(Object error, StackTrace stackTrace) {
48 if (!future._mayComplete) throw new StateError("Future already completed");
49 future._completeError(error, stackTrace); 57 future._completeError(error, stackTrace);
50 } 58 }
51 } 59 }
52 60
53 class _Future<T> implements Future<T> { 61 class _Future<T> implements Future<T> {
54 // State of the future. The state determines the interpretation of the 62 // State of the future. The state determines the interpretation of the
55 // [resultOrListeners] field. 63 // [resultOrListeners] field.
56 // TODO(lrn): rename field since it can also contain a chained future. 64 // TODO(lrn): rename field since it can also contain a chained future.
57 65
58 /// Initial state, waiting for a result. In this state, the 66 /// Initial state, waiting for a result. In this state, the
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 void _markPendingCompletion() { 214 void _markPendingCompletion() {
207 if (!_mayComplete) throw new StateError("Future already completed"); 215 if (!_mayComplete) throw new StateError("Future already completed");
208 _state = _PENDING_COMPLETE; 216 _state = _PENDING_COMPLETE;
209 } 217 }
210 218
211 T get _value { 219 T get _value {
212 assert(_isComplete && _hasValue); 220 assert(_isComplete && _hasValue);
213 return _resultOrListeners; 221 return _resultOrListeners;
214 } 222 }
215 223
216 _AsyncError get _error { 224 AsyncError get _error {
217 assert(_isComplete && _hasError); 225 assert(_isComplete && _hasError);
218 return _resultOrListeners; 226 return _resultOrListeners;
219 } 227 }
220 228
221 void _setValue(T value) { 229 void _setValue(T value) {
222 assert(!_isComplete); // But may have a completion pending. 230 assert(!_isComplete); // But may have a completion pending.
223 _state = _VALUE; 231 _state = _VALUE;
224 _resultOrListeners = value; 232 _resultOrListeners = value;
225 } 233 }
226 234
227 void _setError(Object error, StackTrace stackTrace) { 235 void _setError(Object error, StackTrace stackTrace) {
228 assert(!_isComplete); // But may have a completion pending. 236 assert(!_isComplete); // But may have a completion pending.
229 _state = _ERROR; 237 _state = _ERROR;
230 _resultOrListeners = new _AsyncError(error, stackTrace); 238 _resultOrListeners = new AsyncError(error, stackTrace);
231 } 239 }
232 240
233 void _addListener(_Future listener) { 241 void _addListener(_Future listener) {
234 assert(listener._nextListener == null); 242 assert(listener._nextListener == null);
235 if (_isComplete) { 243 if (_isComplete) {
236 // Handle late listeners asynchronously. 244 // Handle late listeners asynchronously.
237 _zone.scheduleMicrotask(() { 245 _zone.scheduleMicrotask(() {
238 _propagateToListeners(this, listener); 246 _propagateToListeners(this, listener);
239 }); 247 });
240 } else { 248 } else {
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 * 438 *
431 * If [runCallback] is true (which should be the default) it executes 439 * If [runCallback] is true (which should be the default) it executes
432 * the registered action of listeners. If it is `false` then the callback is 440 * the registered action of listeners. If it is `false` then the callback is
433 * skipped. This is used to complete futures with chained futures. 441 * skipped. This is used to complete futures with chained futures.
434 */ 442 */
435 static void _propagateToListeners(_Future source, _Future listeners) { 443 static void _propagateToListeners(_Future source, _Future listeners) {
436 while (true) { 444 while (true) {
437 if (!source._isComplete) return; // Chained future. 445 if (!source._isComplete) return; // Chained future.
438 bool hasError = source._hasError; 446 bool hasError = source._hasError;
439 if (hasError && listeners == null) { 447 if (hasError && listeners == null) {
440 _AsyncError asyncError = source._error; 448 AsyncError asyncError = source._error;
441 source._zone.handleUncaughtError( 449 source._zone.handleUncaughtError(
442 asyncError.error, asyncError.stackTrace); 450 asyncError.error, asyncError.stackTrace);
443 return; 451 return;
444 } 452 }
445 if (listeners == null) return; 453 if (listeners == null) return;
446 _Future listener = listeners; 454 _Future listener = listeners;
447 if (listener._nextListener != null) { 455 if (listener._nextListener != null) {
448 // Usually futures only have one listener. If they have several, we 456 // Usually futures only have one listener. If they have several, we
449 // handle them specially. 457 // handle them specially.
450 _propagateMultipleListeners(source, listeners); 458 _propagateMultipleListeners(source, listeners);
(...skipping 13 matching lines...) Expand all
464 // the future is not already marked (or chained). 472 // the future is not already marked (or chained).
465 // Only if we either have an error or callbacks, go into this, somewhat 473 // Only if we either have an error or callbacks, go into this, somewhat
466 // expensive, branch. Here we'll enter/leave the zone. Many futures 474 // expensive, branch. Here we'll enter/leave the zone. Many futures
467 // doesn't have callbacks, so this is a significant optimization. 475 // doesn't have callbacks, so this is a significant optimization.
468 if (hasError || 476 if (hasError ||
469 listener._onValue != null || 477 listener._onValue != null ||
470 listener._whenCompleteAction != null) { 478 listener._whenCompleteAction != null) {
471 Zone zone = listener._zone; 479 Zone zone = listener._zone;
472 if (hasError && !source._zone.inSameErrorZone(zone)) { 480 if (hasError && !source._zone.inSameErrorZone(zone)) {
473 // Don't cross zone boundaries with errors. 481 // Don't cross zone boundaries with errors.
474 _AsyncError asyncError = source._error; 482 AsyncError asyncError = source._error;
475 source._zone.handleUncaughtError( 483 source._zone.handleUncaughtError(
476 asyncError.error, asyncError.stackTrace); 484 asyncError.error, asyncError.stackTrace);
477 return; 485 return;
478 } 486 }
479 487
480 Zone oldZone; 488 Zone oldZone;
481 if (!identical(Zone.current, zone)) { 489 if (!identical(Zone.current, zone)) {
482 // Change zone if it's not current. 490 // Change zone if it's not current.
483 oldZone = Zone._enter(zone); 491 oldZone = Zone._enter(zone);
484 } 492 }
485 493
486 bool handleValueCallback() { 494 bool handleValueCallback() {
487 try { 495 try {
488 listenerValueOrError = zone.runUnary(listener._onValue, 496 listenerValueOrError = zone.runUnary(listener._onValue,
489 sourceValue); 497 sourceValue);
490 return true; 498 return true;
491 } catch (e, s) { 499 } catch (e, s) {
492 listenerValueOrError = new _AsyncError(e, s); 500 listenerValueOrError = new AsyncError(e, s);
493 return false; 501 return false;
494 } 502 }
495 } 503 }
496 504
497 void handleError() { 505 void handleError() {
498 _AsyncError asyncError = source._error; 506 AsyncError asyncError = source._error;
499 _FutureErrorTest test = listener._errorTest; 507 _FutureErrorTest test = listener._errorTest;
500 bool matchesTest = true; 508 bool matchesTest = true;
501 if (test != null) { 509 if (test != null) {
502 try { 510 try {
503 matchesTest = zone.runUnary(test, asyncError.error); 511 matchesTest = zone.runUnary(test, asyncError.error);
504 } catch (e, s) { 512 } catch (e, s) {
505 // TODO(ajohnsen): Should we suport rethrow for test throws? 513 // TODO(ajohnsen): Should we suport rethrow for test throws?
506 listenerValueOrError = identical(asyncError.error, e) ? 514 listenerValueOrError = identical(asyncError.error, e) ?
507 asyncError : new _AsyncError(e, s); 515 asyncError : new AsyncError(e, s);
508 listenerHasValue = false; 516 listenerHasValue = false;
509 return; 517 return;
510 } 518 }
511 } 519 }
512 Function errorCallback = listener._onError; 520 Function errorCallback = listener._onError;
513 if (matchesTest && errorCallback != null) { 521 if (matchesTest && errorCallback != null) {
514 try { 522 try {
515 if (errorCallback is ZoneBinaryCallback) { 523 if (errorCallback is ZoneBinaryCallback) {
516 listenerValueOrError = zone.runBinary(errorCallback, 524 listenerValueOrError = zone.runBinary(errorCallback,
517 asyncError.error, 525 asyncError.error,
518 asyncError.stackTrace); 526 asyncError.stackTrace);
519 } else { 527 } else {
520 listenerValueOrError = zone.runUnary(errorCallback, 528 listenerValueOrError = zone.runUnary(errorCallback,
521 asyncError.error); 529 asyncError.error);
522 } 530 }
523 } catch (e, s) { 531 } catch (e, s) {
524 listenerValueOrError = identical(asyncError.error, e) ? 532 listenerValueOrError = identical(asyncError.error, e) ?
525 asyncError : new _AsyncError(e, s); 533 asyncError : new AsyncError(e, s);
526 listenerHasValue = false; 534 listenerHasValue = false;
527 return; 535 return;
528 } 536 }
529 listenerHasValue = true; 537 listenerHasValue = true;
530 } else { 538 } else {
531 // Copy over the error from the source. 539 // Copy over the error from the source.
532 listenerValueOrError = asyncError; 540 listenerValueOrError = asyncError;
533 listenerHasValue = false; 541 listenerHasValue = false;
534 } 542 }
535 } 543 }
536 544
537 void handleWhenCompleteCallback() { 545 void handleWhenCompleteCallback() {
538 var completeResult; 546 var completeResult;
539 try { 547 try {
540 completeResult = zone.run(listener._whenCompleteAction); 548 completeResult = zone.run(listener._whenCompleteAction);
541 } catch (e, s) { 549 } catch (e, s) {
542 if (hasError && identical(source._error.error, e)) { 550 if (hasError && identical(source._error.error, e)) {
543 listenerValueOrError = source._error; 551 listenerValueOrError = source._error;
544 } else { 552 } else {
545 listenerValueOrError = new _AsyncError(e, s); 553 listenerValueOrError = new AsyncError(e, s);
546 } 554 }
547 listenerHasValue = false; 555 listenerHasValue = false;
548 } 556 }
549 if (completeResult is Future) { 557 if (completeResult is Future) {
550 listener._isChained = true; 558 listener._isChained = true;
551 isPropagationAborted = true; 559 isPropagationAborted = true;
552 completeResult.then((ignored) { 560 completeResult.then((ignored) {
553 // Try again. Since the future is marked as chained it won't run 561 // Try again. Since the future is marked as chained it won't run
554 // the whenComplete again. 562 // the whenComplete again.
555 _propagateToListeners(source, listener); 563 _propagateToListeners(source, listener);
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 _chainForeignFuture(chainSource, listener); 615 _chainForeignFuture(chainSource, listener);
608 } 616 }
609 return; 617 return;
610 } 618 }
611 } 619 }
612 if (listenerHasValue) { 620 if (listenerHasValue) {
613 listeners = listener._removeListeners(); 621 listeners = listener._removeListeners();
614 listener._setValue(listenerValueOrError); 622 listener._setValue(listenerValueOrError);
615 } else { 623 } else {
616 listeners = listener._removeListeners(); 624 listeners = listener._removeListeners();
617 _AsyncError asyncError = listenerValueOrError; 625 AsyncError asyncError = listenerValueOrError;
618 listener._setError(asyncError.error, asyncError.stackTrace); 626 listener._setError(asyncError.error, asyncError.stackTrace);
619 } 627 }
620 // Prepare for next round. 628 // Prepare for next round.
621 source = listener; 629 source = listener;
622 } 630 }
623 } 631 }
624 632
625 Future timeout(Duration timeLimit, {onTimeout()}) { 633 Future timeout(Duration timeLimit, {onTimeout()}) {
626 if (_isComplete) return new _Future.immediate(this); 634 if (_isComplete) return new _Future.immediate(this);
627 _Future result = new _Future(); 635 _Future result = new _Future();
(...skipping 21 matching lines...) Expand all
649 } 657 }
650 }, onError: (e, s) { 658 }, onError: (e, s) {
651 if (timer.isActive) { 659 if (timer.isActive) {
652 timer.cancel(); 660 timer.cancel();
653 result._completeError(e, s); 661 result._completeError(e, s);
654 } 662 }
655 }); 663 });
656 return result; 664 return result;
657 } 665 }
658 } 666 }
OLDNEW
« no previous file with comments | « sdk/lib/async/future.dart ('k') | sdk/lib/async/stream.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698