mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
ensure _futurize does not leak uncaught errors into the zone (flutter/engine#32070)
This commit is contained in:
parent
ae5d2a1ba7
commit
ab2ea2006c
@ -747,9 +747,98 @@ void hooksTests() {
|
||||
expectEquals(frameNumber, 2);
|
||||
});
|
||||
|
||||
test('_futureize handles callbacker sync error', () async {
|
||||
String? callbacker(void Function(Object? arg) cb) {
|
||||
return 'failure';
|
||||
}
|
||||
Object? error;
|
||||
try {
|
||||
await _futurize(callbacker);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
expectNotEquals(error, null);
|
||||
});
|
||||
|
||||
test('_futureize does not leak sync uncaught exceptions into the zone', () async {
|
||||
String? callbacker(void Function(Object? arg) cb) {
|
||||
cb(null); // indicates failure
|
||||
}
|
||||
Object? error;
|
||||
try {
|
||||
await _futurize(callbacker);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
expectNotEquals(error, null);
|
||||
});
|
||||
|
||||
test('_futureize does not leak async uncaught exceptions into the zone', () async {
|
||||
String? callbacker(void Function(Object? arg) cb) {
|
||||
Timer.run(() {
|
||||
cb(null); // indicates failure
|
||||
});
|
||||
}
|
||||
Object? error;
|
||||
try {
|
||||
await _futurize(callbacker);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
expectNotEquals(error, null);
|
||||
});
|
||||
|
||||
test('_futureize successfully returns a value sync', () async {
|
||||
String? callbacker(void Function(Object? arg) cb) {
|
||||
cb(true);
|
||||
}
|
||||
final Object? result = await _futurize(callbacker);
|
||||
|
||||
expectEquals(result, true);
|
||||
});
|
||||
|
||||
test('_futureize successfully returns a value async', () async {
|
||||
String? callbacker(void Function(Object? arg) cb) {
|
||||
Timer.run(() {
|
||||
cb(true);
|
||||
});
|
||||
}
|
||||
final Object? result = await _futurize(callbacker);
|
||||
|
||||
expectEquals(result, true);
|
||||
});
|
||||
|
||||
_finish();
|
||||
}
|
||||
|
||||
typedef _Callback<T> = void Function(T result);
|
||||
typedef _Callbacker<T> = String? Function(_Callback<T?> callback);
|
||||
|
||||
// This is an exact copy of the function defined in painting.dart. If you change either
|
||||
// then you must change both.
|
||||
Future<T> _futurize<T>(_Callbacker<T> callbacker) {
|
||||
final Completer<T> completer = Completer<T>.sync();
|
||||
// If the callback synchronously throws an error, then synchronously
|
||||
// rethrow that error instead of adding it to the completer. This
|
||||
// prevents the Zone from receiving an uncaught exception.
|
||||
bool sync = true;
|
||||
final String? error = callbacker((T? t) {
|
||||
if (t == null) {
|
||||
if (sync) {
|
||||
throw Exception('operation failed');
|
||||
} else {
|
||||
completer.completeError(Exception('operation failed'));
|
||||
}
|
||||
} else {
|
||||
completer.complete(t);
|
||||
}
|
||||
});
|
||||
sync = false;
|
||||
if (error != null)
|
||||
throw Exception(error);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void _callHook(
|
||||
String name, [
|
||||
int argCount = 0,
|
||||
|
||||
@ -5599,15 +5599,27 @@ typedef _Callbacker<T> = String? Function(_Callback<T?> callback);
|
||||
/// return _futurize(_doSomethingAndCallback);
|
||||
/// }
|
||||
/// ```
|
||||
// Note: this function is not directly tested so that it remains private, instead an exact
|
||||
// copy of it has been inlined into the test at lib/ui/fixtures/ui_test.dart. if you change
|
||||
// this function, then you must update the test.
|
||||
Future<T> _futurize<T>(_Callbacker<T> callbacker) {
|
||||
final Completer<T> completer = Completer<T>.sync();
|
||||
// If the callback synchronously throws an error, then synchronously
|
||||
// rethrow that error instead of adding it to the completer. This
|
||||
// prevents the Zone from receiving an uncaught exception.
|
||||
bool sync = true;
|
||||
final String? error = callbacker((T? t) {
|
||||
if (t == null) {
|
||||
completer.completeError(Exception('operation failed'));
|
||||
if (sync) {
|
||||
throw Exception('operation failed');
|
||||
} else {
|
||||
completer.completeError(Exception('operation failed'));
|
||||
}
|
||||
} else {
|
||||
completer.complete(t);
|
||||
}
|
||||
});
|
||||
sync = false;
|
||||
if (error != null)
|
||||
throw Exception(error);
|
||||
return completer.future;
|
||||
|
||||
@ -139,15 +139,28 @@ final PlatformViewRegistry platformViewRegistry = PlatformViewRegistry();
|
||||
// NNBD migration.
|
||||
typedef _Callback<T> = void Function(T result);
|
||||
typedef _Callbacker<T> = String? Function(_Callback<T> callback);
|
||||
|
||||
// Note: this function is not directly tested so that it remains private, instead an exact
|
||||
// copy of it has been inlined into the test at lib/ui/fixtures/ui_test.dart. if you change
|
||||
// this function, then you must update the test.
|
||||
Future<T> _futurize<T>(_Callbacker<T> callbacker) {
|
||||
final Completer<T> completer = Completer<T>.sync();
|
||||
final String? error = callbacker((T t) {
|
||||
// If the callback synchronously throws an error, then synchronously
|
||||
// rethrow that error instead of adding it to the completer. This
|
||||
// prevents the Zone from receiving an uncaught exception.
|
||||
bool sync = true;
|
||||
final String? error = callbacker((T? t) {
|
||||
if (t == null) {
|
||||
completer.completeError(Exception('operation failed'));
|
||||
if (sync) {
|
||||
throw Exception('operation failed');
|
||||
} else {
|
||||
completer.completeError(Exception('operation failed'));
|
||||
}
|
||||
} else {
|
||||
completer.complete(t);
|
||||
}
|
||||
});
|
||||
sync = false;
|
||||
if (error != null) {
|
||||
throw Exception(error);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user