mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 15:21:54 +00:00
Revert "Refactor _Future
."
This reverts commit69f32d6ad7
. Reason for revert: We seem to have a number of tests failing with timeouts in CBUILD after this change, please see logs at69f32d6ad7
Original change's description: > Refactor `_Future`. > > This is a major rewrite of the `_Future` class, > which is the default implementation of the `Future` interface. > > The main goal was to reduce the number of expensive type checks > in the internal passing around of data. > Expensive type checks are things like > * `is _Future<T>` (more expensive than just `is _Future`, the latter > can be a single class-ID check. > * Covariant generic parameter checks (using `T` covariantly in a > parameter forces a run-time type check). > > Also removed some plain unnecessary casts and turned some > implicit casts from `dynamic` into `unsafeCast`s. > > This seems to be an success, at least on very primitive benchmarks, according to Golem: > FutureCatchErrorTest 41.22% (1.9 noise) > FutureValueTest 46.51% (2.8 noise) > EmptyFutureTest 59.15% (3.1 noise) > FutureWhenCompleteTest 51.10% (3.2 noise) > > A secondary goal was to clean up a very old and messy class, > and make it clearer for other `dart:async` how to interact > with the future. > > The change has a memory cost: The `_FutureListener<S,T>` class, > which represents a `then`, `catchError` or `whenComplete` > call on a `_Future`, now contains a reference to its source future, > the one which provides the inputs to the callbacks, > as well as the result future returned by the call. > That's one extra memory slot per listener. > > In return, the `_FutureListener` now does not need to > get its source future as an argument, which needs a covariant > generic type check, and the methods of `_Future` can be written > in a way which ignores the type parameters of both `_Future` > and `_FutureListener`, which reduces complex type checks > significantly. > > In general, typed code is in `_FutureListener`, which knows both > the source and target types of the listener callbacks, and which > contains the futures already at that type, so no extra type checking > is needed. > The `_Future` class is mostly untyped, except for its "public" > API, called by other classes, which checks inputs, > and code interacting with non-native futures. > Invariants ensure that only correctly typed values > are stored in the untyped shared `_resultOrListeners` field > on `_Future`, as determined by its `_state` integer. > (This was already partially true, and has simply been made > more consistent.) > > Further, we now throw an error in a situation that was previously > unhandled: When a `_Future` is completed with *itself*. > That would ensure that the future would never complete > (it waits for itself to complete before it can complete), > and may potentially have caused weird loops in the representation. > In practice, it probably never happens. Now it makes the error > fail with an error. > Currently a private `_FutureCyclicDependencyError` which presents > as an `UnsupportedError`. > That avoids code like > ```dart > import "dart:async"; > void main() { > var c = Completer(); > c.complete(c.future); // bad. > print("well!"); > var d = Completer(); > d.complete(c.future); > print("shucks!"); > } > ``` > from hanging the runtime by busily searching for the end of a cycle. > > See https://github.com/dart-lang/sdk/issues/48225 > Fixes #48225 > > TEST= refactoring covered by existing tests, few new tests. > > Change-Id: Id9fc5af5fe011deb0af3e1e8a4ea3a91799f9da4 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244241 > Reviewed-by: Martin Kustermann <kustermann@google.com> > Commit-Queue: Lasse Nielsen <lrn@google.com> TBR=lrn@google.com,kustermann@google.com,sra@google.com,sigmund@google.com,nshahan@google.com Change-Id: I455be5a04b4c346df26d4ded0fa7388baccb0f8c No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247762 Reviewed-by: Siva Annamalai <asiva@google.com> Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
parent
83b50a70c8
commit
5cb3b37c74
|
@ -125,8 +125,7 @@ const List<LineException> afterExceptions = const [
|
||||||
const LineException('_asyncAwait.<anonymous function>', 'async_patch.dart'),
|
const LineException('_asyncAwait.<anonymous function>', 'async_patch.dart'),
|
||||||
const LineException('_asyncStart.<anonymous function>', 'async_patch.dart'),
|
const LineException('_asyncStart.<anonymous function>', 'async_patch.dart'),
|
||||||
const LineException('_RootZone.runUnary', 'zone.dart'),
|
const LineException('_RootZone.runUnary', 'zone.dart'),
|
||||||
const LineException('_FutureListener.propagate', 'future_impl.dart'),
|
const LineException('_FutureListener.handleValue', 'future_impl.dart'),
|
||||||
const LineException('_FutureListener.propagateResults', 'future_impl.dart'),
|
|
||||||
const LineException('_Future._completeWithValue', 'future_impl.dart'),
|
const LineException('_Future._completeWithValue', 'future_impl.dart'),
|
||||||
const LineException(
|
const LineException(
|
||||||
'_Future._propagateToListeners.handleValueCallback', 'future_impl.dart'),
|
'_Future._propagateToListeners.handleValueCallback', 'future_impl.dart'),
|
||||||
|
@ -137,7 +136,6 @@ const List<LineException> afterExceptions = const [
|
||||||
const LineException('_startMicrotaskLoop', 'schedule_microtask.dart'),
|
const LineException('_startMicrotaskLoop', 'schedule_microtask.dart'),
|
||||||
const LineException('_AsyncRun._scheduleImmediateJsOverride.internalCallback',
|
const LineException('_AsyncRun._scheduleImmediateJsOverride.internalCallback',
|
||||||
'async_patch.dart'),
|
'async_patch.dart'),
|
||||||
const LineException('Closure.cspForwardCall', 'js_helper.dart'),
|
|
||||||
const LineException('invokeClosure.<anonymous function>', 'js_helper.dart'),
|
const LineException('invokeClosure.<anonymous function>', 'js_helper.dart'),
|
||||||
const LineException('invokeClosure', 'js_helper.dart'),
|
const LineException('invokeClosure', 'js_helper.dart'),
|
||||||
const LineException('convertDartClosureToJS', 'js_helper.dart'),
|
const LineException('convertDartClosureToJS', 'js_helper.dart'),
|
||||||
|
|
|
@ -28,19 +28,19 @@ static method main() → dynamic
|
||||||
|
|
||||||
|
|
||||||
Extra constant evaluation status:
|
Extra constant evaluation status:
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#catchError)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#catchError)
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> ListConstant(const <Type*>[])
|
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> ListConstant(const <Type*>[])
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#test)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#test)
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> SymbolConstant(#whenComplete)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> SymbolConstant(#whenComplete)
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> ListConstant(const <Type*>[])
|
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> ListConstant(const <Type*>[])
|
||||||
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> MapConstant(const <Symbol*, dynamic>{})
|
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> MapConstant(const <Symbol*, dynamic>{})
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#timeout)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#timeout)
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> ListConstant(const <Type*>[])
|
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> ListConstant(const <Type*>[])
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#onTimeout)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#onTimeout)
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#then)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#then)
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#onError)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#onError)
|
||||||
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> SymbolConstant(#asStream)
|
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> SymbolConstant(#asStream)
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const <Type*>[])
|
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const <Type*>[])
|
||||||
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const <dynamic>[])
|
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const <dynamic>[])
|
||||||
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> MapConstant(const <Symbol*, dynamic>{})
|
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> MapConstant(const <Symbol*, dynamic>{})
|
||||||
Extra constant evaluation: evaluated: 61, effectively constant: 15
|
Extra constant evaluation: evaluated: 61, effectively constant: 15
|
||||||
|
|
|
@ -474,28 +474,3 @@ Future<void> runVMTests(
|
||||||
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until the breakpoint map has been updated with the given
|
|
||||||
/// [breakpoint].
|
|
||||||
///
|
|
||||||
/// The `Isolate.addBreakpoint()` call will do a RPC call to the VM and return
|
|
||||||
/// the added breakpoint.
|
|
||||||
///
|
|
||||||
/// Though the `Isolate.breakpoints` list will *not* reflect this immediately.
|
|
||||||
/// This list is updated asynchronously by listening for
|
|
||||||
/// `ServiceEvent.kBreakpointAdded` events from the VM's `kDebugStream` (see
|
|
||||||
/// [VM] class)
|
|
||||||
Future waitUntilBreakpointIsReady(
|
|
||||||
Isolate isolate, Breakpoint breakpoint) async {
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
var breakpoints = isolate.breakpoints;
|
|
||||||
if (breakpoints != null &&
|
|
||||||
breakpoints
|
|
||||||
.any((p) => p.breakpointNumber == breakpoint.breakpointNumber)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await Future.delayed(const Duration(milliseconds: 1));
|
|
||||||
}
|
|
||||||
throw TimeoutException(
|
|
||||||
'The expected breakpoint has not been advertised by the VM in time');
|
|
||||||
}
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ var tests = <IsolateTest>[
|
||||||
LINE_A,
|
LINE_A,
|
||||||
);
|
);
|
||||||
isolate = await service.getIsolate(isolateId);
|
isolate = await service.getIsolate(isolateId);
|
||||||
await waitUntilBreakpointIsReady(isolate, bpt);
|
|
||||||
expect(isolate.breakpoints!.length, 1);
|
expect(isolate.breakpoints!.length, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +73,6 @@ var tests = <IsolateTest>[
|
||||||
LINE_B,
|
LINE_B,
|
||||||
);
|
);
|
||||||
isolate = await service.getIsolate(isolateId);
|
isolate = await service.getIsolate(isolateId);
|
||||||
await waitUntilBreakpointIsReady(isolate, bpt);
|
|
||||||
expect(isolate.breakpoints!.length, 2);
|
expect(isolate.breakpoints!.length, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +86,6 @@ var tests = <IsolateTest>[
|
||||||
LINE_C,
|
LINE_C,
|
||||||
);
|
);
|
||||||
isolate = await service.getIsolate(isolateId);
|
isolate = await service.getIsolate(isolateId);
|
||||||
await waitUntilBreakpointIsReady(isolate, bpt);
|
|
||||||
expect(isolate.breakpoints!.length, 3);
|
expect(isolate.breakpoints!.length, 3);
|
||||||
}
|
}
|
||||||
// Wait for breakpoint events.
|
// Wait for breakpoint events.
|
||||||
|
|
|
@ -94,7 +94,6 @@ var tests = <IsolateTest>[
|
||||||
expect(location.line, 16);
|
expect(location.line, 16);
|
||||||
|
|
||||||
isolate = await service.getIsolate(isolateId);
|
isolate = await service.getIsolate(isolateId);
|
||||||
await waitUntilBreakpointIsReady(isolate, bpt);
|
|
||||||
expect(isolate.breakpoints!.length, 1);
|
expect(isolate.breakpoints!.length, 1);
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
@ -218,7 +217,6 @@ var tests = <IsolateTest>[
|
||||||
|
|
||||||
// Refresh isolate state.
|
// Refresh isolate state.
|
||||||
isolate = await service.getIsolate(isolateId);
|
isolate = await service.getIsolate(isolateId);
|
||||||
await waitUntilBreakpointIsReady(isolate, bpt);
|
|
||||||
expect(isolate.breakpoints!.length, 1);
|
expect(isolate.breakpoints!.length, 1);
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
|
|
@ -95,7 +95,7 @@ final tests = <IsolateTest>[
|
||||||
(VmService service, IsolateRef isolateRef) async {
|
(VmService service, IsolateRef isolateRef) async {
|
||||||
final result = await service.getStack(isolateRef.id!);
|
final result = await service.getStack(isolateRef.id!);
|
||||||
|
|
||||||
expect(result.frames, hasLength(8)); // Must match frames below.
|
expect(result.frames, hasLength(10));
|
||||||
expect(result.asyncCausalFrames, hasLength(26));
|
expect(result.asyncCausalFrames, hasLength(26));
|
||||||
expect(result.awaiterFrames, hasLength(13));
|
expect(result.awaiterFrames, hasLength(13));
|
||||||
|
|
||||||
|
@ -107,6 +107,8 @@ final tests = <IsolateTest>[
|
||||||
[equals('Regular'), anything],
|
[equals('Regular'), anything],
|
||||||
[equals('Regular'), anything],
|
[equals('Regular'), anything],
|
||||||
[equals('Regular'), anything],
|
[equals('Regular'), anything],
|
||||||
|
[equals('Regular'), anything],
|
||||||
|
[equals('Regular'), anything],
|
||||||
[equals('Regular'), endsWith(' _RawReceivePortImpl._handleMessage')],
|
[equals('Regular'), endsWith(' _RawReceivePortImpl._handleMessage')],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -1793,10 +1793,8 @@ class Isolate extends ServiceObjectOwner implements M.Isolate {
|
||||||
case ServiceEvent.kPausePostRequest:
|
case ServiceEvent.kPausePostRequest:
|
||||||
case ServiceEvent.kNone:
|
case ServiceEvent.kNone:
|
||||||
case ServiceEvent.kResume:
|
case ServiceEvent.kResume:
|
||||||
assert(
|
assert((pauseEvent == null) ||
|
||||||
(pauseEvent == null) ||
|
!event.timestamp!.isBefore(pauseEvent!.timestamp));
|
||||||
!event.timestamp!.isBefore(pauseEvent!.timestamp),
|
|
||||||
"${event.timestamp} is before ${pauseEvent!.timestamp}");
|
|
||||||
pauseEvent = createEventFromServiceEvent(event) as M.DebugEvent;
|
pauseEvent = createEventFromServiceEvent(event) as M.DebugEvent;
|
||||||
_updateRunState();
|
_updateRunState();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -54,7 +54,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location!.script.id, equals(script.id));
|
expect(bpt.location!.script.id, equals(script.id));
|
||||||
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
||||||
equals(LINE_A));
|
equals(LINE_A));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +65,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location!.script.id, equals(script.id));
|
expect(bpt.location!.script.id, equals(script.id));
|
||||||
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
||||||
equals(LINE_B));
|
equals(LINE_B));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(2));
|
expect(isolate.breakpoints.length, equals(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +76,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location!.script.id, equals(script.id));
|
expect(bpt.location!.script.id, equals(script.id));
|
||||||
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
expect(bpt.location!.script.tokenToLine(bpt.location!.tokenPos),
|
||||||
equals(LINE_C));
|
equals(LINE_C));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(3));
|
expect(isolate.breakpoints.length, equals(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location!.script.id, equals(script.id));
|
expect(bpt.location!.script.id, equals(script.id));
|
||||||
expect(
|
expect(
|
||||||
bpt.location!.script.tokenToLine(bpt.location!.tokenPos), equals(15));
|
bpt.location!.script.tokenToLine(bpt.location!.tokenPos), equals(15));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, bpt);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
@ -191,7 +190,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location!.script.name, equals('debugging_test.dart'));
|
expect(bpt.location!.script.name, equals('debugging_test.dart'));
|
||||||
expect(
|
expect(
|
||||||
bpt.location!.script.tokenToLine(bpt.location!.tokenPos), equals(12));
|
bpt.location!.script.tokenToLine(bpt.location!.tokenPos), equals(12));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, bpt);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
|
|
@ -53,6 +53,7 @@ var tests = <IsolateTest>[
|
||||||
var frames = stack['frames'];
|
var frames = stack['frames'];
|
||||||
var asyncFrames = stack['asyncCausalFrames'];
|
var asyncFrames = stack['asyncCausalFrames'];
|
||||||
var awaiterFrames = stack['awaiterFrames'];
|
var awaiterFrames = stack['awaiterFrames'];
|
||||||
|
expect(frames.length, greaterThanOrEqualTo(20));
|
||||||
expect(asyncFrames.length, greaterThan(frames.length));
|
expect(asyncFrames.length, greaterThan(frames.length));
|
||||||
expect(awaiterFrames.length, greaterThan(frames.length));
|
expect(awaiterFrames.length, greaterThan(frames.length));
|
||||||
expect(stack['truncated'], false);
|
expect(stack['truncated'], false);
|
||||||
|
|
|
@ -716,23 +716,3 @@ Future runDDSTests(List<String> mainArgs, List<DDSTest> tests,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until the breakpoint map has been updated with the given
|
|
||||||
/// [breakpoint].
|
|
||||||
///
|
|
||||||
/// The `Isolate.addBreakpoint()` call will do a RPC call to the VM and return
|
|
||||||
/// the added breakpoint.
|
|
||||||
///
|
|
||||||
/// Though the `Isolate.breakpoints` map will *not* reflect this immediately.
|
|
||||||
/// This map is updated asynchronously by listening for
|
|
||||||
/// `ServiceEvent.kBreakpointAdded` events from the VM's `kDebugStream` (see
|
|
||||||
/// [VM] class)
|
|
||||||
Future waitUntilBreakpointIsReady(
|
|
||||||
Map<int, Breakpoint> map, Breakpoint breakpoint) async {
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
if (map.containsKey(breakpoint.number)) return;
|
|
||||||
await Future.delayed(const Duration(milliseconds: 1));
|
|
||||||
}
|
|
||||||
throw TimeoutException(
|
|
||||||
'The expected breakpoint has not been advertised by the VM in time');
|
|
||||||
}
|
|
||||||
|
|
|
@ -54,7 +54,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location.script.id, equals(script.id));
|
expect(bpt.location.script.id, equals(script.id));
|
||||||
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
||||||
equals(LINE_A));
|
equals(LINE_A));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +65,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location.script.id, equals(script.id));
|
expect(bpt.location.script.id, equals(script.id));
|
||||||
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
||||||
equals(LINE_B));
|
equals(LINE_B));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(2));
|
expect(isolate.breakpoints.length, equals(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +76,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.location.script.id, equals(script.id));
|
expect(bpt.location.script.id, equals(script.id));
|
||||||
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
|
||||||
equals(LINE_C));
|
equals(LINE_C));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, result);
|
|
||||||
expect(isolate.breakpoints.length, equals(3));
|
expect(isolate.breakpoints.length, equals(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.type, equals('Breakpoint'));
|
expect(bpt.type, equals('Breakpoint'));
|
||||||
expect(bpt.location.script.id, equals(script.id));
|
expect(bpt.location.script.id, equals(script.id));
|
||||||
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(15));
|
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(15));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, bpt);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
@ -189,7 +188,6 @@ var tests = <IsolateTest>[
|
||||||
expect(bpt.type, equals('Breakpoint'));
|
expect(bpt.type, equals('Breakpoint'));
|
||||||
expect(bpt.location.script.name, equals('debugging_test.dart'));
|
expect(bpt.location.script.name, equals('debugging_test.dart'));
|
||||||
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(12));
|
expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(12));
|
||||||
await waitUntilBreakpointIsReady(isolate.breakpoints, bpt);
|
|
||||||
expect(isolate.breakpoints.length, equals(1));
|
expect(isolate.breakpoints.length, equals(1));
|
||||||
|
|
||||||
await completer.future; // Wait for breakpoint events.
|
await completer.future; // Wait for breakpoint events.
|
||||||
|
|
|
@ -53,6 +53,7 @@ var tests = <IsolateTest>[
|
||||||
var frames = stack['frames'];
|
var frames = stack['frames'];
|
||||||
var asyncFrames = stack['asyncCausalFrames'];
|
var asyncFrames = stack['asyncCausalFrames'];
|
||||||
var awaiterFrames = stack['awaiterFrames'];
|
var awaiterFrames = stack['awaiterFrames'];
|
||||||
|
expect(frames.length, greaterThanOrEqualTo(20));
|
||||||
expect(asyncFrames.length, greaterThan(frames.length));
|
expect(asyncFrames.length, greaterThan(frames.length));
|
||||||
expect(awaiterFrames.length, greaterThan(frames.length));
|
expect(awaiterFrames.length, greaterThan(frames.length));
|
||||||
expect(stack['truncated'], false);
|
expect(stack['truncated'], false);
|
||||||
|
|
|
@ -715,23 +715,3 @@ Future runDDSTests(List<String> mainArgs, List<DDSTest> tests,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits until the breakpoint map has been updated with the given
|
|
||||||
/// [breakpoint].
|
|
||||||
///
|
|
||||||
/// The `Isolate.addBreakpoint()` call will do a RPC call to the VM and return
|
|
||||||
/// the added breakpoint.
|
|
||||||
///
|
|
||||||
/// Though the `Isolate.breakpoints` map will *not* reflect this immediately.
|
|
||||||
/// This map is updated asynchronously by listening for
|
|
||||||
/// `ServiceEvent.kBreakpointAdded` events from the VM's `kDebugStream` (see
|
|
||||||
/// [VM] class)
|
|
||||||
Future waitUntilBreakpointIsReady(
|
|
||||||
Map<int, Breakpoint> map, Breakpoint breakpoint) async {
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
if (map.containsKey(breakpoint.number)) return;
|
|
||||||
await Future.delayed(const Duration(milliseconds: 1));
|
|
||||||
}
|
|
||||||
throw TimeoutException(
|
|
||||||
'The expected breakpoint has not been advertised by the VM in time');
|
|
||||||
}
|
|
||||||
|
|
|
@ -281,10 +281,10 @@ namespace dart {
|
||||||
V(::, reachabilityFence, ReachabilityFence, 0x730f2b7f) \
|
V(::, reachabilityFence, ReachabilityFence, 0x730f2b7f) \
|
||||||
V(::, _asyncThenWrapperHelper, AsyncThenWrapperHelper, 0x0c17f838) \
|
V(::, _asyncThenWrapperHelper, AsyncThenWrapperHelper, 0x0c17f838) \
|
||||||
V(_Utf8Decoder, _scan, Utf8DecoderScan, 0xf296c901) \
|
V(_Utf8Decoder, _scan, Utf8DecoderScan, 0xf296c901) \
|
||||||
V(_Future, timeout, FutureTimeout, 0x8d415c97) \
|
V(_Future, timeout, FutureTimeout, 0xa7cb3294) \
|
||||||
V(Future, wait, FutureWait, 0xb0b596bd) \
|
V(Future, wait, FutureWait, 0xb0b596bd) \
|
||||||
V(_RootZone, runUnary, RootZoneRunUnary, 0xb607f8bf) \
|
V(_RootZone, runUnary, RootZoneRunUnary, 0xb607f8bf) \
|
||||||
V(_FutureListener, propagate, FutureListenerPropagate, 0x8a29635e) \
|
V(_FutureListener, handleValue, FutureListenerHandleValue, 0x438115a8) \
|
||||||
V(::, has63BitSmis, Has63BitSmis, 0xf61b56f1) \
|
V(::, has63BitSmis, Has63BitSmis, 0xf61b56f1) \
|
||||||
V(::, get:extensionStreamHasListener, ExtensionStreamHasListener, 0xfab46343)\
|
V(::, get:extensionStreamHasListener, ExtensionStreamHasListener, 0xfab46343)\
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ const intptr_t k_FutureListener_stateCatchError = 2;
|
||||||
// - sdk/lib/async/future_impl.dart:_FutureListener.stateWhenComplete.
|
// - sdk/lib/async/future_impl.dart:_FutureListener.stateWhenComplete.
|
||||||
const intptr_t k_FutureListener_stateWhenComplete = 8;
|
const intptr_t k_FutureListener_stateWhenComplete = 8;
|
||||||
|
|
||||||
// Keep in sync with sdk/lib/async/future_impl.dart:_FutureListener.propagate.
|
// Keep in sync with sdk/lib/async/future_impl.dart:_FutureListener.handleValue.
|
||||||
const intptr_t kNumArgsFutureListenerPropagate = 0;
|
const intptr_t kNumArgsFutureListenerHandleValue = 1;
|
||||||
|
|
||||||
// Find current yield index from async closure.
|
// Find current yield index from async closure.
|
||||||
// Async closures contains a variable, :await_jump_var that holds the index into
|
// Async closures contains a variable, :await_jump_var that holds the index into
|
||||||
|
@ -538,19 +538,19 @@ ClosurePtr StackTraceUtils::ClosureFromFrameFunction(
|
||||||
return caller_closure_finder->FindCaller(closure);
|
return caller_closure_finder->FindCaller(closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// May have been called from `_FutureListener.propagate`, which means its
|
// May have been called from `_FutureListener.handleValue`, which means its
|
||||||
// receiver holds the Future chain.
|
// receiver holds the Future chain.
|
||||||
DartFrameIterator future_frames(frames);
|
DartFrameIterator future_frames(frames);
|
||||||
if (function.recognized_kind() == MethodRecognizer::kRootZoneRunUnary) {
|
if (function.recognized_kind() == MethodRecognizer::kRootZoneRunUnary) {
|
||||||
frame = future_frames.NextFrame();
|
frame = future_frames.NextFrame();
|
||||||
function = frame->LookupDartFunction();
|
function = frame->LookupDartFunction();
|
||||||
if (function.recognized_kind() !=
|
if (function.recognized_kind() !=
|
||||||
MethodRecognizer::kFutureListenerPropagate) {
|
MethodRecognizer::kFutureListenerHandleValue) {
|
||||||
return Closure::null();
|
return Closure::null();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (function.recognized_kind() ==
|
if (function.recognized_kind() ==
|
||||||
MethodRecognizer::kFutureListenerPropagate) {
|
MethodRecognizer::kFutureListenerHandleValue) {
|
||||||
*is_async = true;
|
*is_async = true;
|
||||||
*skip_frame = true;
|
*skip_frame = true;
|
||||||
|
|
||||||
|
@ -558,9 +558,9 @@ ClosurePtr StackTraceUtils::ClosureFromFrameFunction(
|
||||||
// before the arguments to the call.
|
// before the arguments to the call.
|
||||||
Object& receiver =
|
Object& receiver =
|
||||||
Object::Handle(*(reinterpret_cast<ObjectPtr*>(frame->GetCallerSp()) +
|
Object::Handle(*(reinterpret_cast<ObjectPtr*>(frame->GetCallerSp()) +
|
||||||
kNumArgsFutureListenerPropagate));
|
kNumArgsFutureListenerHandleValue));
|
||||||
if (receiver.ptr() == Symbols::OptimizedOut().ptr()) {
|
if (receiver.ptr() == Symbols::OptimizedOut().ptr()) {
|
||||||
// In the very rare case that _FutureListener.propagate has deoptimized
|
// In the very rare case that _FutureListener.handleValue has deoptimized
|
||||||
// it may override the receiver slot in the caller frame with "<optimized
|
// it may override the receiver slot in the caller frame with "<optimized
|
||||||
// out>" due to the `this` no longer being needed.
|
// out>" due to the `this` no longer being needed.
|
||||||
return Closure::null();
|
return Closure::null();
|
||||||
|
|
|
@ -27,7 +27,7 @@ _async<T>(Function() initGenerator) {
|
||||||
late Object? Function(Object?) onValue;
|
late Object? Function(Object?) onValue;
|
||||||
late Object Function(Object, StackTrace?) onError;
|
late Object Function(Object, StackTrace?) onError;
|
||||||
|
|
||||||
_Future<Object?> onAwait(Object? value) {
|
onAwait(Object? value) {
|
||||||
_Future<Object?> f;
|
_Future<Object?> f;
|
||||||
if (value is _Future) {
|
if (value is _Future) {
|
||||||
f = value;
|
f = value;
|
||||||
|
@ -84,15 +84,31 @@ _async<T>(Function() initGenerator) {
|
||||||
var iteratorValue = JS('', '#.next(null)', iter);
|
var iteratorValue = JS('', '#.next(null)', iter);
|
||||||
var value = JS('', '#.value', iteratorValue);
|
var value = JS('', '#.value', iteratorValue);
|
||||||
if (JS<bool>('!', '#.done', iteratorValue)) {
|
if (JS<bool>('!', '#.done', iteratorValue)) {
|
||||||
if (isRunningAsEvent) {
|
// TODO(jmesserly): this is a workaround for ignored cast failures.
|
||||||
asyncFuture._completeUnchecked(value);
|
// Remove it once we've fixed those. We should be able to call:
|
||||||
|
//
|
||||||
|
// if (isRunningAsEvent) {
|
||||||
|
// asyncFuture._complete(value);
|
||||||
|
// } else {
|
||||||
|
// asyncFuture._asyncComplete(value);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// But if the user code returns `Future<dynamic>` instead of
|
||||||
|
// `Future<T>`, that function won't recognize it as a future and will
|
||||||
|
// instead treat it as a completed value.
|
||||||
|
if (value is Future) {
|
||||||
|
if (value is _Future) {
|
||||||
|
_Future._chainCoreFuture(value, asyncFuture);
|
||||||
|
} else {
|
||||||
|
asyncFuture._chainForeignFuture(value);
|
||||||
|
}
|
||||||
|
} else if (isRunningAsEvent) {
|
||||||
|
asyncFuture._completeWithValue(JS('', '#', value));
|
||||||
} else {
|
} else {
|
||||||
asyncFuture._asyncCompleteUnchecked(value);
|
asyncFuture._asyncComplete(JS('', '#', value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_Future<dynamic> awaitedFuture = onAwait(value); // _Future<Object?>.
|
_Future._chainCoreFuture(onAwait(value), asyncFuture);
|
||||||
awaitedFuture.then(asyncFuture._completeWithValueUnchecked,
|
|
||||||
onError: asyncFuture._completeError);
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
if (isRunningAsEvent) {
|
if (isRunningAsEvent) {
|
||||||
|
|
|
@ -198,20 +198,27 @@ class _AsyncAwaitCompleter<T> implements Completer<T> {
|
||||||
|
|
||||||
void complete([FutureOr<T>? value]) {
|
void complete([FutureOr<T>? value]) {
|
||||||
// All paths require that if value is null, null as T succeeds.
|
// All paths require that if value is null, null as T succeeds.
|
||||||
value ??= value as T;
|
value = (value == null) ? value as T : value;
|
||||||
if (!isSync) {
|
if (!isSync) {
|
||||||
_future._asyncCompleteUnchecked(value);
|
_future._asyncComplete(value);
|
||||||
|
} else if (value is Future<T>) {
|
||||||
|
assert(!_future._isComplete);
|
||||||
|
_future._chainFuture(value);
|
||||||
} else {
|
} else {
|
||||||
_future._completeUnchecked(value);
|
// TODO(40014): Remove cast when type promotion works.
|
||||||
|
// This would normally be `as T` but we use `as dynamic` to make the
|
||||||
|
// unneeded check be implicit to match dart2js unsound optimizations in
|
||||||
|
// the user code.
|
||||||
|
_future._completeWithValue(value as dynamic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void completeError(Object e, [StackTrace? st]) {
|
void completeError(Object e, [StackTrace? st]) {
|
||||||
var error = AsyncError(e, st);
|
st ??= AsyncError.defaultStackTrace(e);
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
_future._completeErrorObject(error);
|
_future._completeError(e, st);
|
||||||
} else {
|
} else {
|
||||||
_future._asyncCompleteErrorObject(error);
|
_future._asyncCompleteError(e, st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,7 @@ class _AsyncStarStreamController<T> {
|
||||||
if ((future != null) && future._mayComplete) {
|
if ((future != null) && future._mayComplete) {
|
||||||
// If the stream has been cancelled, complete the cancellation future
|
// If the stream has been cancelled, complete the cancellation future
|
||||||
// with the error.
|
// with the error.
|
||||||
future._completeWithValueUnchecked(null);
|
future._completeWithValue(null);
|
||||||
}
|
}
|
||||||
controller.close();
|
controller.close();
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ void _completeOnAsyncReturn(_Future _future, Object? value, bool is_sync) {
|
||||||
if (!is_sync || value is Future) {
|
if (!is_sync || value is Future) {
|
||||||
_future._asyncCompleteUnchecked(value);
|
_future._asyncCompleteUnchecked(value);
|
||||||
} else {
|
} else {
|
||||||
_future._completeWithValueUnchecked(value);
|
_future._completeWithValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,9 +303,9 @@ void _completeWithNoFutureOnAsyncReturn(
|
||||||
// allow then and error handlers to be attached.
|
// allow then and error handlers to be attached.
|
||||||
// async_jump_var=0 is prior to first await, =1 is first await.
|
// async_jump_var=0 is prior to first await, =1 is first await.
|
||||||
if (!is_sync) {
|
if (!is_sync) {
|
||||||
_future._asyncCompleteWithValueUnchecked(value);
|
_future._asyncCompleteUncheckedNoFuture(value);
|
||||||
} else {
|
} else {
|
||||||
_future._completeWithValueUnchecked(value);
|
_future._completeWithValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,6 @@ import "dart:_internal"
|
||||||
CastStream,
|
CastStream,
|
||||||
CastStreamTransformer,
|
CastStreamTransformer,
|
||||||
checkNotNullable,
|
checkNotNullable,
|
||||||
checkUnsoundType,
|
|
||||||
EmptyIterator,
|
EmptyIterator,
|
||||||
IterableElementError,
|
IterableElementError,
|
||||||
nullFuture,
|
nullFuture,
|
||||||
|
|
|
@ -303,13 +303,18 @@ abstract class Future<T> {
|
||||||
if (result is Future<T>) {
|
if (result is Future<T>) {
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return _Future<T>().._setValue(result);
|
// TODO(40014): Remove cast when type promotion works.
|
||||||
|
return new _Future<T>.value(result as dynamic);
|
||||||
}
|
}
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
var currentZone = Zone._current;
|
var future = new _Future<T>();
|
||||||
return _Future<T>.zone(currentZone)
|
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
|
||||||
.._asyncCompleteErrorObject(
|
if (replacement != null) {
|
||||||
_interceptSyncError(currentZone, error, stackTrace));
|
future._asyncCompleteError(replacement.error, replacement.stackTrace);
|
||||||
|
} else {
|
||||||
|
future._asyncCompleteError(error, stackTrace);
|
||||||
|
}
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,10 +346,7 @@ abstract class Future<T> {
|
||||||
@pragma("vm:entry-point")
|
@pragma("vm:entry-point")
|
||||||
@pragma("vm:prefer-inline")
|
@pragma("vm:prefer-inline")
|
||||||
factory Future.value([FutureOr<T>? value]) {
|
factory Future.value([FutureOr<T>? value]) {
|
||||||
if (value != null) {
|
return new _Future<T>.immediate(value == null ? value as T : value);
|
||||||
return _Future<T>().._asyncCompleteUnchecked(value);
|
|
||||||
}
|
|
||||||
return _Future<T>().._setValue(value as T);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a future that completes with an error.
|
/// Creates a future that completes with an error.
|
||||||
|
@ -367,10 +369,15 @@ abstract class Future<T> {
|
||||||
factory Future.error(Object error, [StackTrace? stackTrace]) {
|
factory Future.error(Object error, [StackTrace? stackTrace]) {
|
||||||
// TODO(40614): Remove once non-nullability is sound.
|
// TODO(40614): Remove once non-nullability is sound.
|
||||||
checkNotNullable(error, "error");
|
checkNotNullable(error, "error");
|
||||||
var currentZone = Zone._current;
|
if (!identical(Zone.current, _rootZone)) {
|
||||||
return _Future<T>.zone(currentZone)
|
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
|
||||||
.._asyncCompleteErrorObject(
|
if (replacement != null) {
|
||||||
_interceptSyncError(currentZone, error, stackTrace));
|
error = replacement.error;
|
||||||
|
stackTrace = replacement.stackTrace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stackTrace ??= AsyncError.defaultStackTrace(error);
|
||||||
|
return new _Future<T>.immediateError(error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a future that runs its computation after a delay.
|
/// Creates a future that runs its computation after a delay.
|
||||||
|
@ -1223,13 +1230,28 @@ abstract class Completer<T> {
|
||||||
// for error replacement and missing stack trace first.
|
// for error replacement and missing stack trace first.
|
||||||
void _completeWithErrorCallback(
|
void _completeWithErrorCallback(
|
||||||
_Future result, Object error, StackTrace? stackTrace) {
|
_Future result, Object error, StackTrace? stackTrace) {
|
||||||
result._completeErrorObject(
|
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
|
||||||
_interceptSyncError(result._zone, error, stackTrace));
|
if (replacement != null) {
|
||||||
|
error = replacement.error;
|
||||||
|
stackTrace = replacement.stackTrace;
|
||||||
|
} else {
|
||||||
|
stackTrace ??= AsyncError.defaultStackTrace(error);
|
||||||
|
}
|
||||||
|
result._completeError(error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like [_completeWithErrorCallback] but completes asynchronously.
|
// Like [_completeWithErrorCallback] but completes asynchronously.
|
||||||
void _asyncCompleteWithErrorCallback(
|
void _asyncCompleteWithErrorCallback(
|
||||||
_Future result, Object error, StackTrace? stackTrace) {
|
_Future result, Object error, StackTrace? stackTrace) {
|
||||||
result._asyncCompleteErrorObject(
|
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
|
||||||
_interceptSyncError(result._zone, error, stackTrace));
|
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);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1079,15 +1079,14 @@ class _StreamIterator<T> implements StreamIterator<T> {
|
||||||
|
|
||||||
void _onDone() {
|
void _onDone() {
|
||||||
var subscription = _subscription;
|
var subscription = _subscription;
|
||||||
_Future moveNextFuture = _stateData as dynamic;
|
_Future<bool> moveNextFuture = _stateData as dynamic;
|
||||||
assert(moveNextFuture is _Future<bool>);
|
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
_stateData = null;
|
_stateData = null;
|
||||||
if (subscription != null) {
|
if (subscription != null) {
|
||||||
moveNextFuture._completeWithValue(false);
|
moveNextFuture._completeWithValue(false);
|
||||||
} else {
|
} else {
|
||||||
// Event delivered during `listen` call.
|
// Event delivered during `listen` call.
|
||||||
moveNextFuture._asyncCompleteWithValueUnchecked(false);
|
moveNextFuture._asyncCompleteWithValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
part of dart.async;
|
part of dart.async;
|
||||||
|
|
||||||
/// Runs user code and takes actions depending on success or failure.
|
/// Runs user code and takes actions depending on success or failure.
|
||||||
void _runUserCode<T>(T userCode(), onSuccess(T value),
|
_runUserCode<T>(T userCode(), onSuccess(T value),
|
||||||
onError(Object error, StackTrace stackTrace)) {
|
onError(Object error, StackTrace stackTrace)) {
|
||||||
try {
|
try {
|
||||||
onSuccess(userCode());
|
onSuccess(userCode());
|
||||||
|
|
|
@ -762,17 +762,6 @@ T checkNotNullable<T extends Object>(T value, String name) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in asserts to check that [object] has *unsound* type [T].
|
|
||||||
///
|
|
||||||
/// Throws if [object] does not have type [T] in sound null safe mode,
|
|
||||||
/// or it does not have type `T?` in unsound null safe mode.
|
|
||||||
///
|
|
||||||
/// Returns `true` if it doesn't throw, so it can be used in an `assert`.
|
|
||||||
bool checkUnsoundType<T>(Object? object) {
|
|
||||||
object as T;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [TypeError] thrown by [checkNotNullable].
|
/// A [TypeError] thrown by [checkNotNullable].
|
||||||
class NotNullableError<T> extends Error implements TypeError {
|
class NotNullableError<T> extends Error implements TypeError {
|
||||||
final String _name;
|
final String _name;
|
||||||
|
|
|
@ -4981,6 +4981,57 @@ Value:
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"li",
|
"li",
|
||||||
{
|
{
|
||||||
|
|
|
@ -4981,6 +4981,57 @@ Value:
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
"style": "padding-left: 13px;"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{},
|
||||||
|
[
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
"style": ""
|
||||||
|
},
|
||||||
|
"<DART_SDK>"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"li",
|
"li",
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Copyright (c) 2022, 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:async_helper/async_helper.dart';
|
|
||||||
import "package:expect/expect.dart";
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
asyncStart();
|
|
||||||
var completer = Completer<int>();
|
|
||||||
// Should complete with error, but not synchronously.
|
|
||||||
completer.complete(completer.future);
|
|
||||||
await completer.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also if going through indirections.
|
|
||||||
var completer1 = Completer<int>();
|
|
||||||
var completer2 = Completer<int>();
|
|
||||||
|
|
||||||
// Should complete with error, but not synchronously.
|
|
||||||
completer1.complete(completer2.future);
|
|
||||||
completer2.complete(completer1.future);
|
|
||||||
|
|
||||||
completer1.future.ignore();
|
|
||||||
completer2.future.ignore();
|
|
||||||
await completer1.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
await completer2.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also if coming from a callback.
|
|
||||||
completer1 = Completer<int>();
|
|
||||||
completer2 = Completer<int>();
|
|
||||||
completer2.complete(completer1.future.then((_) => completer2.future));
|
|
||||||
completer1.complete(1);
|
|
||||||
await completer2.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
asyncEnd();
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Copyright (c) 2022, 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:async_helper/async_helper.dart';
|
|
||||||
import "package:expect/expect.dart";
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
asyncStart();
|
|
||||||
var completer = Completer<int>();
|
|
||||||
// Should complete with error, but not synchronously.
|
|
||||||
completer.complete(completer.future);
|
|
||||||
await completer.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also if going through indirections.
|
|
||||||
var completer1 = Completer<int>();
|
|
||||||
var completer2 = Completer<int>();
|
|
||||||
|
|
||||||
// Should complete with error, but not synchronously.
|
|
||||||
completer1.complete(completer2.future);
|
|
||||||
completer2.complete(completer1.future);
|
|
||||||
|
|
||||||
completer1.future.ignore();
|
|
||||||
completer2.future.ignore();
|
|
||||||
await completer1.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
await completer2.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also if coming from a callback.
|
|
||||||
completer1 = Completer<int>();
|
|
||||||
completer2 = Completer<int>();
|
|
||||||
completer2.complete(completer1.future.then((_) => completer2.future));
|
|
||||||
completer1.complete(1);
|
|
||||||
await completer2.future.then((value) {
|
|
||||||
Expect.fail("Completed with value $value.");
|
|
||||||
}, onError: (e, _) {
|
|
||||||
Expect.type<UnsupportedError>(e);
|
|
||||||
}).timeout(const Duration(milliseconds: 1), onTimeout: () {
|
|
||||||
Expect.fail("Did not complete");
|
|
||||||
});
|
|
||||||
|
|
||||||
asyncEnd();
|
|
||||||
}
|
|
Loading…
Reference in a new issue