Skip to content

Commit 50dfd13

Browse files
author
Jonah Williams
authored
[flutter_tools] prevent hot reload/restart if device has not finished devFS initialization (flutter#73420)
1 parent a8247e7 commit 50dfd13

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

packages/flutter_tools/lib/src/run_hot.dart

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'package:vm_service/vm_service.dart' as vm_service;
88
import 'package:meta/meta.dart';
99
import 'package:pool/pool.dart';
1010

11-
import 'base/async_guard.dart';
1211
import 'base/context.dart';
1312
import 'base/file_system.dart';
1413
import 'base/logger.dart';
@@ -105,6 +104,7 @@ class HotRunner extends ResidentRunner {
105104
/// reload process do not have this issue.
106105
bool _swap = false;
107106

107+
/// Whether the resident runner has correctly attached to the running application.
108108
bool _didAttach = false;
109109

110110
final Map<String, List<int>> benchmarkData = <String, List<int>>{};
@@ -121,7 +121,6 @@ class HotRunner extends ResidentRunner {
121121
bool force = false,
122122
bool pause = false,
123123
}) async {
124-
// TODO(cbernaschina): check that isolateId is the id of the UI isolate.
125124
final OperationResult result = await restart(pause: pause);
126125
if (!result.isOk) {
127126
throw vm_service.RPCError(
@@ -469,8 +468,6 @@ class HotRunner extends ResidentRunner {
469468
String reason,
470469
}) async {
471470
final Stopwatch restartTimer = Stopwatch()..start();
472-
// TODO(aam): Add generator reset logic once we switch to using incremental
473-
// compiler for full application recompilation on restart.
474471
final UpdateFSReport updatedDevFS = await _updateDevFS(fullRestart: true);
475472
if (!updatedDevFS.success) {
476473
for (final FlutterDevice device in flutterDevices) {
@@ -592,6 +589,9 @@ class HotRunner extends ResidentRunner {
592589
bool silent = false,
593590
bool pause = false,
594591
}) async {
592+
if (flutterDevices.any((FlutterDevice device) => device.devFS == null)) {
593+
return OperationResult(1, 'Device initialization has not completed.');
594+
}
595595
String targetPlatform;
596596
String sdkName;
597597
bool emulator;
@@ -666,13 +666,7 @@ class HotRunner extends ResidentRunner {
666666
if (!(await hotRunnerConfig.setupHotRestart())) {
667667
return OperationResult(1, 'setupHotRestart failed');
668668
}
669-
// The current implementation of the vmservice and JSON rpc may throw
670-
// unhandled exceptions into the zone that cannot be caught with a regular
671-
// try catch. The usage is [asyncGuard] is required to normalize the error
672-
// handling, at least until we can refactor the underlying code.
673-
result = await asyncGuard(() => _restartFromSources(
674-
reason: reason,
675-
));
669+
result = await _restartFromSources(reason: reason,);
676670
if (!result.isOk) {
677671
restartEvent = 'restart-failed';
678672
}

packages/flutter_tools/test/general.shard/hot_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ void main() {
343343
when(mockDevice.supportsHotRestart).thenReturn(true);
344344
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
345345
final List<FlutterDevice> devices = <FlutterDevice>[
346-
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug),
346+
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = MockDevFs(),
347347
];
348348
final OperationResult result = await HotRunner(
349349
devices,

packages/flutter_tools/test/general.shard/resident_runner_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,37 @@ void main() {
541541
Usage: () => MockUsage(),
542542
}));
543543

544+
testUsingContext('ResidentRunner fails its operation if the device initialization is not complete', () => testbed.run(() async {
545+
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
546+
listViews,
547+
listViews,
548+
setAssetBundlePath,
549+
]);
550+
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
551+
return 'Example';
552+
});
553+
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
554+
return TargetPlatform.android_arm;
555+
});
556+
when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async {
557+
return false;
558+
});
559+
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
560+
final Completer<void> onAppStart = Completer<void>.sync();
561+
unawaited(residentRunner.attach(
562+
appStartedCompleter: onAppStart,
563+
connectionInfoCompleter: onConnectionInfo,
564+
));
565+
await onAppStart.future;
566+
when(mockFlutterDevice.devFS).thenReturn(null);
567+
568+
final OperationResult result = await residentRunner.restart(fullRestart: false);
569+
expect(result.fatal, false);
570+
expect(result.code, 1);
571+
expect(result.message, contains('Device initialization has not completed.'));
572+
expect(fakeVmServiceHost.hasRemainingExpectations, false);
573+
}));
574+
544575
testUsingContext('ResidentRunner can handle an reload-barred exception from hot reload', () => testbed.run(() async {
545576
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
546577
listViews,

0 commit comments

Comments
 (0)