Unverified Commit 3300a1bd authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] eagerly set asset directory path, cache flutter views,...

[flutter_tools] eagerly set asset directory path, cache flutter views, simplify error handling (#68978)

Performs some small cleanup on the hot reload code path.

- Combines nested try/catch into single try catch, update on clause now that package:vm_service is used and Map does not need to be caught.
- Cache FlutterViews for the lifetime of the hot reload method handler
- Set asset directory path once during startup and remove conditional set during hot reload
parent 95563ff1
...@@ -110,7 +110,6 @@ class HotRunner extends ResidentRunner { ...@@ -110,7 +110,6 @@ class HotRunner extends ResidentRunner {
final Map<String, List<int>> benchmarkData = <String, List<int>>{}; final Map<String, List<int>> benchmarkData = <String, List<int>>{};
DateTime firstBuildTime; DateTime firstBuildTime;
bool _shouldResetAssetDirectory = true;
void _addBenchmarkData(String name, int value) { void _addBenchmarkData(String name, int value) {
benchmarkData[name] ??= <int>[]; benchmarkData[name] ??= <int>[];
...@@ -205,7 +204,7 @@ class HotRunner extends ResidentRunner { ...@@ -205,7 +204,7 @@ class HotRunner extends ResidentRunner {
), ),
); );
} }
} on Exception catch (error) { } on DevFSException catch (error) {
globals.printError('Error initializing DevFS: $error'); globals.printError('Error initializing DevFS: $error');
return 3; return 3;
} }
...@@ -226,6 +225,14 @@ class HotRunner extends ResidentRunner { ...@@ -226,6 +225,14 @@ class HotRunner extends ResidentRunner {
device.generator.accept(); device.generator.accept();
} }
final List<FlutterView> views = await device.vmService.getFlutterViews(); final List<FlutterView> views = await device.vmService.getFlutterViews();
final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri(globals.fs.path.toUri(getAssetBuildDirectory()));
await Future.wait<void>(views.map<Future<void>>(
(FlutterView view) => device.vmService.setAssetDirectory(
assetsDirectory: deviceAssetsDirectoryUri,
uiIsolateId: view.uiIsolate.id,
viewId: view.id,
)
));
for (final FlutterView view in views) { for (final FlutterView view in views) {
globals.printTrace('Connected to $view.'); globals.printTrace('Connected to $view.');
} }
...@@ -729,7 +736,25 @@ class HotRunner extends ResidentRunner { ...@@ -729,7 +736,25 @@ class HotRunner extends ResidentRunner {
); );
}, },
); );
} on vm_service.RPCError { } on vm_service.RPCError catch (error) {
String errorMessage = 'hot reload failed to complete';
int errorCode = 1;
if (error.code == kIsolateReloadBarred) {
errorCode = error.code;
errorMessage = 'Unable to hot reload application due to an unrecoverable error in '
'the source code. Please address the error and then use "R" to '
'restart the app.\n'
'${error.message} (error code: ${error.code})';
HotEvent('reload-barred',
targetPlatform: targetPlatform,
sdkName: sdkName,
emulator: emulator,
fullRestart: false,
reason: reason,
nullSafety: usageNullSafety,
fastReassemble: null,
).send();
} else {
HotEvent('exception', HotEvent('exception',
targetPlatform: targetPlatform, targetPlatform: targetPlatform,
sdkName: sdkName, sdkName: sdkName,
...@@ -739,7 +764,8 @@ class HotRunner extends ResidentRunner { ...@@ -739,7 +764,8 @@ class HotRunner extends ResidentRunner {
reason: reason, reason: reason,
fastReassemble: null, fastReassemble: null,
).send(); ).send();
return OperationResult(1, 'hot reload failed to complete', fatal: true); }
return OperationResult(errorCode, errorMessage, fatal: true);
} finally { } finally {
status.cancel(); status.cancel();
} }
...@@ -764,20 +790,6 @@ class HotRunner extends ResidentRunner { ...@@ -764,20 +790,6 @@ class HotRunner extends ResidentRunner {
]; ];
} }
Future<void> _resetAssetDirectory(FlutterDevice device) async {
final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri(
globals.fs.path.toUri(getAssetBuildDirectory()));
assert(deviceAssetsDirectoryUri != null);
final List<FlutterView> views = await device.vmService.getFlutterViews();
await Future.wait<void>(views.map<Future<void>>(
(FlutterView view) => device.vmService.setAssetDirectory(
assetsDirectory: deviceAssetsDirectoryUri,
uiIsolateId: view.uiIsolate.id,
viewId: view.id,
)
));
}
Future<OperationResult> _reloadSources({ Future<OperationResult> _reloadSources({
String targetPlatform, String targetPlatform,
String sdkName, String sdkName,
...@@ -786,8 +798,10 @@ class HotRunner extends ResidentRunner { ...@@ -786,8 +798,10 @@ class HotRunner extends ResidentRunner {
String reason, String reason,
void Function(String message) onSlow, void Function(String message) onSlow,
}) async { }) async {
final Map<FlutterDevice, List<FlutterView>> viewCache = <FlutterDevice, List<FlutterView>>{};
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
final List<FlutterView> views = await device.vmService.getFlutterViews(); final List<FlutterView> views = await device.vmService.getFlutterViews();
viewCache[device] = views;
for (final FlutterView view in views) { for (final FlutterView view in views) {
if (view.uiIsolate == null) { if (view.uiIsolate == null) {
return OperationResult(2, 'Application isolate not found', fatal: true); return OperationResult(2, 'Application isolate not found', fatal: true);
...@@ -806,20 +820,15 @@ class HotRunner extends ResidentRunner { ...@@ -806,20 +820,15 @@ class HotRunner extends ResidentRunner {
} }
String reloadMessage; String reloadMessage;
final Stopwatch vmReloadTimer = Stopwatch()..start(); final Stopwatch vmReloadTimer = Stopwatch()..start();
Map<String, dynamic> firstReloadDetails; Map<String, Object> firstReloadDetails;
try {
const String entryPath = 'main.dart.incremental.dill'; const String entryPath = 'main.dart.incremental.dill';
final List<Future<DeviceReloadReport>> allReportsFutures = <Future<DeviceReloadReport>>[]; final List<Future<DeviceReloadReport>> allReportsFutures = <Future<DeviceReloadReport>>[];
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
if (_shouldResetAssetDirectory) {
// Asset directory has to be set only once when the engine switches from
// running from bundle to uploaded files.
await _resetAssetDirectory(device);
_shouldResetAssetDirectory = false;
}
final List<Future<vm_service.ReloadReport>> reportFutures = await _reloadDeviceSources( final List<Future<vm_service.ReloadReport>> reportFutures = await _reloadDeviceSources(
device, device,
entryPath, pause: pause, entryPath,
pause: pause,
); );
allReportsFutures.add(Future.wait(reportFutures).then( allReportsFutures.add(Future.wait(reportFutures).then(
(List<vm_service.ReloadReport> reports) async { (List<vm_service.ReloadReport> reports) async {
...@@ -864,45 +873,20 @@ class HotRunner extends ResidentRunner { ...@@ -864,45 +873,20 @@ class HotRunner extends ResidentRunner {
globals.printTrace('reloaded $loadedLibraryCount of $finalLibraryCount libraries'); globals.printTrace('reloaded $loadedLibraryCount of $finalLibraryCount libraries');
reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries'; reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries';
} }
} on Map<String, dynamic> catch (error, stackTrace) {
globals.printTrace('Hot reload failed: $error\n$stackTrace');
final int errorCode = error['code'] as int;
String errorMessage = error['message'] as String;
if (errorCode == kIsolateReloadBarred) {
errorMessage = 'Unable to hot reload application due to an unrecoverable error in '
'the source code. Please address the error and then use "R" to '
'restart the app.\n'
'$errorMessage (error code: $errorCode)';
HotEvent('reload-barred',
targetPlatform: targetPlatform,
sdkName: sdkName,
emulator: emulator,
fullRestart: false,
reason: reason,
nullSafety: usageNullSafety,
fastReassemble: null,
).send();
return OperationResult(errorCode, errorMessage);
}
return OperationResult(errorCode, '$errorMessage (error code: $errorCode)');
} on Exception catch (error, stackTrace) {
globals.printTrace('Hot reload failed: $error\n$stackTrace');
return OperationResult(1, '$error');
}
// Record time it took for the VM to reload the sources. // Record time it took for the VM to reload the sources.
_addBenchmarkData('hotReloadVMReloadMilliseconds', vmReloadTimer.elapsed.inMilliseconds); _addBenchmarkData('hotReloadVMReloadMilliseconds', vmReloadTimer.elapsed.inMilliseconds);
final Stopwatch reassembleTimer = Stopwatch()..start(); final Stopwatch reassembleTimer = Stopwatch()..start();
await _evictDirtyAssets(); await _evictDirtyAssets();
// Check if any isolates are paused and reassemble those // Check if any isolates are paused and reassemble those that aren't.
// that aren't.
final Map<FlutterView, vm_service.VmService> reassembleViews = <FlutterView, vm_service.VmService>{}; final Map<FlutterView, vm_service.VmService> reassembleViews = <FlutterView, vm_service.VmService>{};
final List<Future<void>> reassembleFutures = <Future<void>>[]; final List<Future<void>> reassembleFutures = <Future<void>>[];
String serviceEventKind; String serviceEventKind;
int pausedIsolatesFound = 0; int pausedIsolatesFound = 0;
bool failedReassemble = false; bool failedReassemble = false;
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
final List<FlutterView> views = await device.vmService.getFlutterViews(); final List<FlutterView> views = viewCache[device];
for (final FlutterView view in views) { for (final FlutterView view in views) {
// Check if the isolate is paused, and if so, don't reassemble. Ignore the // Check if the isolate is paused, and if so, don't reassemble. Ignore the
// PostPauseEvent event - the client requesting the pause will resume the app. // PostPauseEvent event - the client requesting the pause will resume the app.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment