Commit 9fdd4f47 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[fuchsia_reload] Reload each instance of a module. (#9379)

parent ea71bdca
...@@ -40,15 +40,24 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -40,15 +40,24 @@ class FuchsiaReloadCommand extends FlutterCommand {
help: 'Path to Fuchsia source tree.'); help: 'Path to Fuchsia source tree.');
argParser.addOption('gn-target', argParser.addOption('gn-target',
abbr: 'g', abbr: 'g',
help: 'GN target of the application, e.g //path/to/app:app'); help: 'GN target of the application, e.g //path/to/app:app.');
argParser.addFlag('list',
abbr: 'l',
defaultsTo: false,
help: 'Lists the running modules. '
'Requires the flags --address(-a) and --fuchsia-root(-f).');
argParser.addOption('name-override', argParser.addOption('name-override',
abbr: 'n', abbr: 'n',
help: 'On-device name of the application binary.'); help: 'On-device name of the application binary.');
argParser.addOption('isolate-number',
abbr: 'i',
help: 'To reload only one instance, speficy the isolate number, e.g. '
'the number in foo\$main-###### given by --list.');
argParser.addOption('target', argParser.addOption('target',
abbr: 't', abbr: 't',
defaultsTo: flx.defaultMainPath, defaultsTo: flx.defaultMainPath,
help: 'Target app path / main entry-point file. ' help: 'Target app path / main entry-point file. '
'Relative to --gn-target path, e.g. lib/main.dart'); 'Relative to --gn-target path, e.g. lib/main.dart.');
} }
@override @override
...@@ -61,11 +70,14 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -61,11 +70,14 @@ class FuchsiaReloadCommand extends FlutterCommand {
String _projectRoot; String _projectRoot;
String _projectName; String _projectName;
String _binaryName; String _binaryName;
String _isolateNumber;
String _fuchsiaProjectPath; String _fuchsiaProjectPath;
String _target; String _target;
String _address; String _address;
String _dotPackagesPath; String _dotPackagesPath;
bool _list;
@override @override
Future<Null> runCommand() async { Future<Null> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
...@@ -79,9 +91,14 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -79,9 +91,14 @@ class FuchsiaReloadCommand extends FlutterCommand {
for (int port in servicePorts) for (int port in servicePorts)
printTrace('Fuchsia service port: $port'); printTrace('Fuchsia service port: $port');
if (_list) {
await _listViews(servicePorts);
return;
}
// Check that there are running VM services on the returned // Check that there are running VM services on the returned
// ports, and find the Isolates that are running the target app. // ports, and find the Isolates that are running the target app.
final String isolateName = '$_binaryName\$main'; final String isolateName = '$_binaryName\$main$_isolateNumber';
final List<int> targetPorts = await _filterPorts(servicePorts, isolateName); final List<int> targetPorts = await _filterPorts(servicePorts, isolateName);
if (targetPorts.isEmpty) if (targetPorts.isEmpty)
throwToolExit('No VMs found running $_binaryName.'); throwToolExit('No VMs found running $_binaryName.');
...@@ -90,43 +107,55 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -90,43 +107,55 @@ class FuchsiaReloadCommand extends FlutterCommand {
// Set up a device and hot runner and attach the hot runner to the first // Set up a device and hot runner and attach the hot runner to the first
// vm service we found. // vm service we found.
final int firstPort = targetPorts[0]; final List<String> fullAddresses = targetPorts.map(
final String fullAddress = '$_address:$firstPort'; (int p) => '$_address:$p'
final FuchsiaDevice device = new FuchsiaDevice(fullAddress); ).toList();
final FuchsiaDevice device = new FuchsiaDevice(fullAddresses[0]);
final HotRunner hotRunner = new HotRunner( final HotRunner hotRunner = new HotRunner(
device, device,
debuggingOptions: new DebuggingOptions.enabled(getBuildMode()), debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
target: _target, target: _target,
projectRootPath: _fuchsiaProjectPath, projectRootPath: _fuchsiaProjectPath,
packagesFilePath: _dotPackagesPath, packagesFilePath: _dotPackagesPath
); );
final Uri observatoryUri = Uri.parse('http://$fullAddress'); final List<Uri> observatoryUris =
printStatus('Connecting to $_binaryName at $observatoryUri'); fullAddresses.map((String a) => Uri.parse('http://$a')).toList();
await hotRunner.attach(observatoryUri, isolateFilter: isolateName); printStatus('Connecting to $_binaryName');
await hotRunner.attach(observatoryUris, isolateFilter: isolateName);
} }
// Find ports where there is a view isolate with the given name Future<List<FlutterView>> _getViews(List<int> ports) async {
Future<List<int>> _filterPorts(List<int> ports, String isolateFilter) async { final List<FlutterView> views = <FlutterView>[];
final List<int> result = <int>[];
for (int port in ports) { for (int port in ports) {
final String addr = 'http://$_address:$port'; final String addr = 'http://$_address:$port';
final Uri uri = Uri.parse(addr); final Uri uri = Uri.parse(addr);
final VMService vmService = VMService.connect(uri); final VMService vmService = VMService.connect(uri);
await vmService.getVM(); await vmService.getVM();
await vmService.waitForViews(); await vmService.waitForViews();
if (vmService.vm.firstView == null) { views.addAll(vmService.vm.views);
printTrace('Found no views at $addr'); }
continue; return views;
} }
for (FlutterView v in vmService.vm.views) {
// Find ports where there is a view isolate with the given name
Future<List<int>> _filterPorts(List<int> ports, String isolateFilter) async {
final List<int> result = <int>[];
for (FlutterView v in await _getViews(ports)) {
final Uri addr = v.owner.vmService.httpAddress;
printTrace('At $addr, found view: ${v.uiIsolate.name}'); printTrace('At $addr, found view: ${v.uiIsolate.name}');
if (v.uiIsolate.name.indexOf(isolateFilter) == 0) if (v.uiIsolate.name.indexOf(isolateFilter) == 0)
result.add(port); result.add(addr.port);
}
} }
return result; return result;
} }
Future<Null> _listViews(List<int> ports) async {
for (FlutterView v in await _getViews(ports)) {
final Uri addr = v.owner.vmService.httpAddress;
printStatus('At $addr, found view: ${v.uiIsolate.name}');
}
}
void _validateArguments() { void _validateArguments() {
_fuchsiaRoot = argResults['fuchsia-root']; _fuchsiaRoot = argResults['fuchsia-root'];
if (_fuchsiaRoot == null) if (_fuchsiaRoot == null)
...@@ -138,6 +167,12 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -138,6 +167,12 @@ class FuchsiaReloadCommand extends FlutterCommand {
if (_address == null) if (_address == null)
throwToolExit('Give the address of the device running Fuchsia with --address.'); throwToolExit('Give the address of the device running Fuchsia with --address.');
_list = argResults['list'];
if (_list) {
// For --list, we only need the device address and the Fuchsia tree root.
return;
}
final List<String> gnTarget = _extractPathAndName(argResults['gn-target']); final List<String> gnTarget = _extractPathAndName(argResults['gn-target']);
_projectRoot = gnTarget[0]; _projectRoot = gnTarget[0];
_projectName = gnTarget[1]; _projectName = gnTarget[1];
...@@ -166,6 +201,13 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -166,6 +201,13 @@ class FuchsiaReloadCommand extends FlutterCommand {
} else { } else {
_binaryName = nameOverride; _binaryName = nameOverride;
} }
final String isolateNumber = argResults['isolate-number'];
if (isolateNumber == null) {
_isolateNumber = '';
} else {
_isolateNumber = '-$isolateNumber';
}
} }
List<String> _extractPathAndName(String gnTarget) { List<String> _extractPathAndName(String gnTarget) {
......
...@@ -64,8 +64,7 @@ abstract class ResidentRunner { ...@@ -64,8 +64,7 @@ abstract class ResidentRunner {
bool get isRunningRelease => debuggingOptions.buildMode == BuildMode.release; bool get isRunningRelease => debuggingOptions.buildMode == BuildMode.release;
bool get supportsServiceProtocol => isRunningDebug || isRunningProfile; bool get supportsServiceProtocol => isRunningDebug || isRunningProfile;
VMService vmService; List<VMService> vmServices;
FlutterView currentView;
StreamSubscription<String> _loggingSubscription; StreamSubscription<String> _loggingSubscription;
/// Start the app and keep the process running during its lifetime. /// Start the app and keep the process running during its lifetime.
...@@ -78,6 +77,25 @@ abstract class ResidentRunner { ...@@ -78,6 +77,25 @@ abstract class ResidentRunner {
bool get supportsRestart => false; bool get supportsRestart => false;
String isolateFilter;
List<FlutterView> _currentViewsCache;
List<FlutterView> get currentViews {
if (_currentViewsCache == null) {
if ((vmServices == null) || vmServices.isEmpty)
return null;
if (isolateFilter == null)
return vmServices[0].vm.views.toList();
final List<FlutterView> result = <FlutterView>[];
for (VMService service in vmServices)
result.addAll(service.vm.allViewsWithName(isolateFilter));
_currentViewsCache = result;
}
return _currentViewsCache;
}
FlutterView get currentView => (currentViews != null) ? currentViews[0] : null;
Future<OperationResult> restart({ bool fullRestart: false, bool pauseAfterRestart: false }) { Future<OperationResult> restart({ bool fullRestart: false, bool pauseAfterRestart: false }) {
throw 'unsupported'; throw 'unsupported';
} }
...@@ -94,21 +112,26 @@ abstract class ResidentRunner { ...@@ -94,21 +112,26 @@ abstract class ResidentRunner {
appFinished(); appFinished();
} }
Future<Null> refreshViews() async {
if ((vmServices == null) || vmServices.isEmpty)
return;
for (VMService service in vmServices)
await service.vm.refreshViews();
_currentViewsCache = null;
}
Future<Null> _debugDumpApp() async { Future<Null> _debugDumpApp() async {
if (vmService != null) await refreshViews();
await vmService.vm.refreshViews();
await currentView.uiIsolate.flutterDebugDumpApp(); await currentView.uiIsolate.flutterDebugDumpApp();
} }
Future<Null> _debugDumpRenderTree() async { Future<Null> _debugDumpRenderTree() async {
if (vmService != null) await refreshViews();
await vmService.vm.refreshViews();
await currentView.uiIsolate.flutterDebugDumpRenderTree(); await currentView.uiIsolate.flutterDebugDumpRenderTree();
} }
Future<Null> _debugToggleDebugPaintSizeEnabled() async { Future<Null> _debugToggleDebugPaintSizeEnabled() async {
if (vmService != null) await refreshViews();
await vmService.vm.refreshViews();
await currentView.uiIsolate.flutterToggleDebugPaintSizeEnabled(); await currentView.uiIsolate.flutterToggleDebugPaintSizeEnabled();
} }
...@@ -117,8 +140,7 @@ abstract class ResidentRunner { ...@@ -117,8 +140,7 @@ abstract class ResidentRunner {
final File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png'); final File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png');
try { try {
if (supportsServiceProtocol && isRunningDebug) { if (supportsServiceProtocol && isRunningDebug) {
if (vmService != null) await refreshViews();
await vmService.vm.refreshViews();
try { try {
await currentView.uiIsolate.flutterDebugAllowBanner(false); await currentView.uiIsolate.flutterDebugAllowBanner(false);
} catch (error) { } catch (error) {
...@@ -148,8 +170,7 @@ abstract class ResidentRunner { ...@@ -148,8 +170,7 @@ abstract class ResidentRunner {
} }
Future<String> _debugRotatePlatform() async { Future<String> _debugRotatePlatform() async {
if (vmService != null) await refreshViews();
await vmService.vm.refreshViews();
switch (await currentView.uiIsolate.flutterPlatformOverride()) { switch (await currentView.uiIsolate.flutterPlatformOverride()) {
case 'iOS': case 'iOS':
return await currentView.uiIsolate.flutterPlatformOverride('android'); return await currentView.uiIsolate.flutterPlatformOverride('android');
...@@ -209,25 +230,37 @@ abstract class ResidentRunner { ...@@ -209,25 +230,37 @@ abstract class ResidentRunner {
_loggingSubscription = null; _loggingSubscription = null;
} }
Future<Null> connectToServiceProtocol(Uri uri, {String isolateFilter}) async { Future<Null> connectToServiceProtocol(
if (!debuggingOptions.debuggingEnabled) List<Uri> uris, {
String isolateFilter
}) async {
if (!debuggingOptions.debuggingEnabled) {
return new Future<Null>.error('Error the service protocol is not enabled.'); return new Future<Null>.error('Error the service protocol is not enabled.');
vmService = VMService.connect(uri); }
printTrace('Connected to service protocol: $uri'); final List<VMService> services = new List<VMService>(uris.length);
await vmService.getVM(); for (int i = 0; i < uris.length; i++) {
services[i] = VMService.connect(uris[i]);
printTrace('Connected to service protocol: ${uris[i]}');
}
vmServices = services;
for (VMService service in services)
await service.getVM();
this.isolateFilter = isolateFilter;
// Refresh the view list, and wait a bit for the list to populate. // Refresh the view list, and wait a bit for the list to populate.
await vmService.waitForViews(); for (VMService service in services)
currentView = (isolateFilter == null) await service.waitForViews();
? vmService.vm.firstView
: vmService.vm.firstViewWithName(isolateFilter);
if (currentView == null) if (currentView == null)
throwToolExit('No Flutter view is available'); throwToolExit('No Flutter view is available');
// Listen for service protocol connection to close. // Listen for service protocol connection to close.
vmService.done.then<Null>( for (VMService service in services) {
service.done.then<Null>(
_serviceProtocolDone, _serviceProtocolDone,
onError: _serviceProtocolError).whenComplete(appFinished); onError: _serviceProtocolError
).whenComplete(appFinished);
}
} }
Future<Null> _serviceProtocolDone(dynamic object) { Future<Null> _serviceProtocolDone(dynamic object) {
...@@ -357,7 +390,10 @@ abstract class ResidentRunner { ...@@ -357,7 +390,10 @@ abstract class ResidentRunner {
Future<Null> preStop() async { } Future<Null> preStop() async { }
Future<Null> stopApp() async { Future<Null> stopApp() async {
if (vmService != null && !vmService.isClosed) { if (vmServices != null &&
vmServices.isNotEmpty &&
!vmServices[0].isClosed) {
// TODO(zra): iterate over all the views.
if ((currentView != null) && (currentView.uiIsolate != null)) { if ((currentView != null) && (currentView.uiIsolate != null)) {
// TODO(johnmccutchan): Wait for the exit command to complete. // TODO(johnmccutchan): Wait for the exit command to complete.
currentView.uiIsolate.flutterExit(); currentView.uiIsolate.flutterExit();
......
...@@ -101,27 +101,27 @@ class ColdRunner extends ResidentRunner { ...@@ -101,27 +101,27 @@ class ColdRunner extends ResidentRunner {
// Connect to observatory. // Connect to observatory.
if (debuggingOptions.debuggingEnabled) if (debuggingOptions.debuggingEnabled)
await connectToServiceProtocol(_result.observatoryUri); await connectToServiceProtocol(<Uri>[_result.observatoryUri]);
if (_result.hasObservatory) { if (_result.hasObservatory) {
connectionInfoCompleter?.complete(new DebugConnectionInfo( connectionInfoCompleter?.complete(new DebugConnectionInfo(
httpUri: _result.observatoryUri, httpUri: _result.observatoryUri,
wsUri: vmService.wsAddress, wsUri: vmServices[0].wsAddress,
)); ));
} }
printTrace('Application running.'); printTrace('Application running.');
if (vmService != null) { if (vmServices != null && vmServices.isNotEmpty) {
device.getLogReader(app: package).appPid = vmService.vm.pid; device.getLogReader(app: package).appPid = vmServices[0].vm.pid;
await vmService.vm.refreshViews(); await refreshViews();
printTrace('Connected to ${vmService.vm.firstView}\.'); printTrace('Connected to $currentView.');
} }
if (vmService != null && traceStartup) { if (vmServices != null && vmServices.isNotEmpty && traceStartup) {
printStatus('Downloading startup trace info...'); printStatus('Downloading startup trace info...');
try { try {
await downloadStartupTrace(vmService); await downloadStartupTrace(vmServices[0]);
} catch(error) { } catch(error) {
printError(error); printError(error);
return 2; return 2;
...@@ -174,7 +174,7 @@ class ColdRunner extends ResidentRunner { ...@@ -174,7 +174,7 @@ class ColdRunner extends ResidentRunner {
@override @override
Future<Null> preStop() async { Future<Null> preStop() async {
// If we're running in release mode, stop the app using the device logic. // If we're running in release mode, stop the app using the device logic.
if (vmService == null) if (vmServices == null || vmServices.isEmpty)
await device.stopApp(package); await device.stopApp(package);
} }
} }
...@@ -87,21 +87,21 @@ class HotRunner extends ResidentRunner { ...@@ -87,21 +87,21 @@ class HotRunner extends ResidentRunner {
return true; return true;
} }
Future<int> attach(Uri observatoryUri, { Future<int> attach(List<Uri> observatoryUris, {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<Null> appStartedCompleter, Completer<Null> appStartedCompleter,
String isolateFilter, String isolateFilter,
}) async { }) async {
_observatoryUri = observatoryUri; _observatoryUri = observatoryUris[0];
try { try {
await connectToServiceProtocol( await connectToServiceProtocol(
_observatoryUri, isolateFilter: isolateFilter); observatoryUris, isolateFilter: isolateFilter);
} catch (error) { } catch (error) {
printError('Error connecting to the service protocol: $error'); printError('Error connecting to the service protocol: $error');
return 2; return 2;
} }
device.getLogReader(app: package).appPid = vmService.vm.pid; device.getLogReader(app: package).appPid = vmServices[0].vm.pid;
try { try {
final Uri baseUri = await _initDevFS(); final Uri baseUri = await _initDevFS();
...@@ -109,7 +109,7 @@ class HotRunner extends ResidentRunner { ...@@ -109,7 +109,7 @@ class HotRunner extends ResidentRunner {
connectionInfoCompleter.complete( connectionInfoCompleter.complete(
new DebugConnectionInfo( new DebugConnectionInfo(
httpUri: _observatoryUri, httpUri: _observatoryUri,
wsUri: vmService.wsAddress, wsUri: vmServices[0].wsAddress,
baseUri: baseUri.toString() baseUri: baseUri.toString()
) )
); );
...@@ -124,8 +124,9 @@ class HotRunner extends ResidentRunner { ...@@ -124,8 +124,9 @@ class HotRunner extends ResidentRunner {
return 3; return 3;
} }
await vmService.vm.refreshViews(); await refreshViews();
printTrace('Connected to $currentView.'); for (FlutterView view in currentViews)
printTrace('Connected to $view.');
if (stayResident) { if (stayResident) {
setupTerminal(); setupTerminal();
...@@ -140,7 +141,7 @@ class HotRunner extends ResidentRunner { ...@@ -140,7 +141,7 @@ class HotRunner extends ResidentRunner {
// Measure time to perform a hot restart. // Measure time to perform a hot restart.
printStatus('Benchmarking hot restart'); printStatus('Benchmarking hot restart');
await restart(fullRestart: true); await restart(fullRestart: true);
await vmService.vm.refreshViews(); await refreshViews();
// TODO(johnmccutchan): Modify script entry point. // TODO(johnmccutchan): Modify script entry point.
printStatus('Benchmarking hot reload'); printStatus('Benchmarking hot reload');
// Measure time to perform a hot reload. // Measure time to perform a hot reload.
...@@ -219,7 +220,7 @@ class HotRunner extends ResidentRunner { ...@@ -219,7 +220,7 @@ class HotRunner extends ResidentRunner {
return 2; return 2;
} }
return attach(result.observatoryUri, return attach(<Uri>[result.observatoryUri],
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter); appStartedCompleter: appStartedCompleter);
} }
...@@ -241,7 +242,7 @@ class HotRunner extends ResidentRunner { ...@@ -241,7 +242,7 @@ class HotRunner extends ResidentRunner {
Future<Uri> _initDevFS() { Future<Uri> _initDevFS() {
final String fsName = fs.path.basename(projectRootPath); final String fsName = fs.path.basename(projectRootPath);
_devFS = new DevFS(vmService, _devFS = new DevFS(vmServices[0],
fsName, fsName,
fs.directory(projectRootPath), fs.directory(projectRootPath),
packagesFilePath: packagesFilePath); packagesFilePath: packagesFilePath);
...@@ -425,11 +426,18 @@ class HotRunner extends ResidentRunner { ...@@ -425,11 +426,18 @@ class HotRunner extends ResidentRunner {
final Uri devicePackagesUri = _devFS.baseUri.resolve('.packages'); final Uri devicePackagesUri = _devFS.baseUri.resolve('.packages');
if (benchmarkMode) if (benchmarkMode)
vmReloadTimer.start(); vmReloadTimer.start();
final Map<String, dynamic> reloadReport =
await currentView.uiIsolate.reloadSources( Map<String, dynamic> reloadReport;
for (FlutterView view in currentViews) {
final Map<String, dynamic> report = await view.uiIsolate.reloadSources(
pause: pause, pause: pause,
rootLibUri: deviceEntryUri, rootLibUri: deviceEntryUri,
packagesUri: devicePackagesUri); packagesUri: devicePackagesUri
);
// Just take the first one until we think of something smart to do.
if (reloadReport == null)
reloadReport = report;
}
if (!validateReloadReport(reloadReport)) { if (!validateReloadReport(reloadReport)) {
// Reload failed. // Reload failed.
flutterUsage.sendEvent('hot', 'reload-reject'); flutterUsage.sendEvent('hot', 'reload-reject');
...@@ -464,29 +472,43 @@ class HotRunner extends ResidentRunner { ...@@ -464,29 +472,43 @@ class HotRunner extends ResidentRunner {
if (benchmarkMode) if (benchmarkMode)
reassembleTimer.start(); reassembleTimer.start();
// Reload the isolate. // Reload the isolate.
await currentView.uiIsolate.reload(); for (FlutterView view in currentViews)
await view.uiIsolate.reload();
// We are now running from source. // We are now running from source.
_runningFromSnapshot = false; _runningFromSnapshot = false;
// Check if the isolate is paused. // Check if the isolate is paused.
final ServiceEvent pauseEvent = currentView.uiIsolate.pauseEvent;
final List<FlutterView> reassembleViews = <FlutterView>[];
for (FlutterView view in currentViews) {
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
if ((pauseEvent != null) && (pauseEvent.isPauseEvent)) { if ((pauseEvent != null) && (pauseEvent.isPauseEvent)) {
// Isolate is paused. Stop here. // Isolate is paused. Don't reassemble.
printTrace('Skipping reassemble because isolate is paused.'); continue;
}
reassembleViews.add(view);
}
if (reassembleViews.isEmpty) {
printTrace('Skipping reassemble because all isolates are paused.');
return new OperationResult(OperationResult.ok.code, reloadMessage); return new OperationResult(OperationResult.ok.code, reloadMessage);
} }
await _evictDirtyAssets(); await _evictDirtyAssets();
printTrace('Reassembling application'); printTrace('Reassembling application');
bool reassembleAndScheduleErrors = false;
for (FlutterView view in reassembleViews) {
try { try {
await currentView.uiIsolate.flutterReassemble(); await view.uiIsolate.flutterReassemble();
} catch (_) { } catch (error) {
printError('Reassembling application failed.'); reassembleAndScheduleErrors = true;
return new OperationResult(1, 'error reassembling application'); printError('Reassembling ${view.uiIsolate.name} failed: $error');
continue;
} }
try { try {
/* ensure that a frame is scheduled */ /* ensure that a frame is scheduled */
await currentView.uiIsolate.uiWindowScheduleFrame(); await view.uiIsolate.uiWindowScheduleFrame();
} catch (_) { } catch (error) {
/* ignore any errors */ reassembleAndScheduleErrors = true;
printError('Scheduling a frame for ${view.uiIsolate.name} failed: $error');
}
} }
reloadTimer.stop(); reloadTimer.stop();
printTrace('Hot reload performed in ' printTrace('Hot reload performed in '
...@@ -503,7 +525,10 @@ class HotRunner extends ResidentRunner { ...@@ -503,7 +525,10 @@ class HotRunner extends ResidentRunner {
} }
if (shouldReportReloadTime) if (shouldReportReloadTime)
flutterUsage.sendTiming('hot', 'reload', reloadTimer.elapsed); flutterUsage.sendTiming('hot', 'reload', reloadTimer.elapsed);
return new OperationResult(OperationResult.ok.code, reloadMessage); return new OperationResult(
reassembleAndScheduleErrors ? 1 : OperationResult.ok.code,
reloadMessage
);
} }
@override @override
......
...@@ -764,13 +764,12 @@ class VM extends ServiceObjectOwner { ...@@ -764,13 +764,12 @@ class VM extends ServiceObjectOwner {
return _viewCache.values.isEmpty ? null : _viewCache.values.first; return _viewCache.values.isEmpty ? null : _viewCache.values.first;
} }
FlutterView firstViewWithName(String isolateFilter) { List<FlutterView> allViewsWithName(String isolateFilter) {
if (_viewCache.values.isEmpty) { if (_viewCache.values.isEmpty)
return null; return null;
} return _viewCache.values.where(
return _viewCache.values.firstWhere( (FlutterView v) => v.uiIsolate.name.contains(isolateFilter)
(FlutterView v) => v.uiIsolate.name.contains(isolateFilter), ).toList();
orElse: () => null);
} }
} }
......
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