Avoid type checks in Future.wait

CoreLibraryReviewExempt: Bypassing because Wasm-approver is OOO.
Change-Id: I484e900c14f181f56083b6e90a0d1f34546c85e1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341504
Commit-Queue: Lasse Nielsen <lrn@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2024-02-13 16:07:12 +00:00 committed by Commit Queue
parent a261196ea7
commit 712df96202

View file

@ -480,34 +480,39 @@ abstract interface class Future<T> {
final _Future<List<T>> _future = _Future<List<T>>();
List<T?>? values; // Collects the values. Set to null on error.
int remaining = 0; // How many futures are we waiting for.
late Object error; // The first error from a future.
late StackTrace stackTrace; // The stackTrace that came with the error.
Object? error; // The first error from a future.
StackTrace? stackTrace; // The stackTrace that came with the error.
// Handle an error from any of the futures.
void handleError(Object theError, StackTrace theStackTrace) {
remaining--;
var remainingResults = --remaining;
List<T?>? valueList = values;
if (valueList != null) {
// First error, set state to represent error having already happened.
values = null;
error = theError;
stackTrace = theStackTrace;
// Then clean up any already successfully produced results.
if (cleanUp != null) {
for (var value in valueList) {
if (value != null) {
// Ensure errors from cleanUp are uncaught.
// Ensure errors from `cleanUp` are uncaught.
T cleanUpValue = value;
new Future.sync(() {
Future.sync(() {
cleanUp(cleanUpValue);
});
}
}
}
values = null;
if (remaining == 0 || eagerError) {
if (remainingResults == 0 || eagerError) {
_future._completeError(theError, theStackTrace);
} else {
error = theError;
stackTrace = theStackTrace;
}
} else if (remaining == 0 && !eagerError) {
_future._completeError(error, stackTrace);
} else {
// Not the first error.
if (remainingResults == 0 && !eagerError) {
// Last future completed, non-eagerly report the first error.
_future._completeError(error!, stackTrace!);
}
}
}
@ -517,24 +522,27 @@ abstract interface class Future<T> {
for (var future in futures) {
int pos = remaining;
future.then((T value) {
remaining--;
var remainingResults = --remaining;
List<T?>? valueList = values;
if (valueList != null) {
// No errors yet.
assert(valueList[pos] == null);
valueList[pos] = value;
if (remaining == 0) {
_future._completeWithValue(List<T>.from(valueList));
if (remainingResults == 0) {
_future._completeWithValue(
[for (var value in valueList) value as T]);
}
} else {
// Prior error, clean-up this value if necessary.
if (cleanUp != null && value != null) {
// Ensure errors from cleanUp are uncaught.
new Future.sync(() {
Future.sync(() {
cleanUp(value);
});
}
if (remaining == 0 && !eagerError) {
// If eagerError is false, and valueList is null, then
// error and stackTrace have been set in handleError above.
_future._completeError(error, stackTrace);
if (remainingResults == 0 && !eagerError) {
// Last future completed, non-eagerly report the first error.
_future._completeError(error!, stackTrace!);
}
}
}, onError: handleError);
@ -544,9 +552,10 @@ abstract interface class Future<T> {
remaining++;
}
if (remaining == 0) {
// No elements in iterable.
return _future.._completeWithValue(<T>[]);
}
values = new List<T?>.filled(remaining, null);
values = List<T?>.filled(remaining, null);
} catch (e, st) {
// The error must have been thrown while iterating over the futures
// list, or while installing a callback handler on the future.
@ -1338,8 +1347,5 @@ void _asyncCompleteWithErrorCallback(
} else {
stackTrace ??= AsyncError.defaultStackTrace(error);
}
if (stackTrace == null) {
throw "unreachable"; // TODO(lrn): Remove when type promotion works.
}
result._asyncCompleteError(error, stackTrace);
}