Unverified Commit b91aedb1 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

Fix StateError during hot reload when no Dart isolates found (#130537)

Fixes https://github.com/flutter/flutter/issues/116262
parent f5a5581f
...@@ -91,7 +91,7 @@ class HotRunner extends ResidentRunner { ...@@ -91,7 +91,7 @@ class HotRunner extends ResidentRunner {
this.multidexEnabled = false, this.multidexEnabled = false,
super.devtoolsHandler, super.devtoolsHandler,
StopwatchFactory stopwatchFactory = const StopwatchFactory(), StopwatchFactory stopwatchFactory = const StopwatchFactory(),
ReloadSourcesHelper reloadSourcesHelper = _defaultReloadSourcesHelper, ReloadSourcesHelper reloadSourcesHelper = defaultReloadSourcesHelper,
ReassembleHelper reassembleHelper = _defaultReassembleHelper, ReassembleHelper reassembleHelper = _defaultReassembleHelper,
}) : _stopwatchFactory = stopwatchFactory, }) : _stopwatchFactory = stopwatchFactory,
_reloadSourcesHelper = reloadSourcesHelper, _reloadSourcesHelper = reloadSourcesHelper,
...@@ -1174,7 +1174,8 @@ typedef ReloadSourcesHelper = Future<OperationResult> Function( ...@@ -1174,7 +1174,8 @@ typedef ReloadSourcesHelper = Future<OperationResult> Function(
String? reason, String? reason,
); );
Future<OperationResult> _defaultReloadSourcesHelper( @visibleForTesting
Future<OperationResult> defaultReloadSourcesHelper(
HotRunner hotRunner, HotRunner hotRunner,
List<FlutterDevice?> flutterDevices, List<FlutterDevice?> flutterDevices,
bool? pause, bool? pause,
...@@ -1186,7 +1187,7 @@ Future<OperationResult> _defaultReloadSourcesHelper( ...@@ -1186,7 +1187,7 @@ Future<OperationResult> _defaultReloadSourcesHelper(
) async { ) async {
final Stopwatch vmReloadTimer = Stopwatch()..start(); final Stopwatch vmReloadTimer = Stopwatch()..start();
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) {
final List<Future<vm_service.ReloadReport>> reportFutures = await _reloadDeviceSources( final List<Future<vm_service.ReloadReport>> reportFutures = await _reloadDeviceSources(
...@@ -1194,10 +1195,13 @@ Future<OperationResult> _defaultReloadSourcesHelper( ...@@ -1194,10 +1195,13 @@ Future<OperationResult> _defaultReloadSourcesHelper(
entryPath, entryPath,
pause: pause, pause: pause,
); );
allReportsFutures.add(Future.wait(reportFutures).then( allReportsFutures.add(Future.wait(reportFutures).then<DeviceReloadReport?>(
(List<vm_service.ReloadReport> reports) async { (List<vm_service.ReloadReport> reports) async {
// TODO(aam): Investigate why we are validating only first reload report, // TODO(aam): Investigate why we are validating only first reload report,
// which seems to be current behavior // which seems to be current behavior
if (reports.isEmpty) {
return null;
}
final vm_service.ReloadReport firstReport = reports.first; final vm_service.ReloadReport firstReport = reports.first;
// Don't print errors because they will be printed further down when // Don't print errors because they will be printed further down when
// `validateReloadReport` is called again. // `validateReloadReport` is called again.
...@@ -1208,9 +1212,9 @@ Future<OperationResult> _defaultReloadSourcesHelper( ...@@ -1208,9 +1212,9 @@ Future<OperationResult> _defaultReloadSourcesHelper(
}, },
)); ));
} }
final List<DeviceReloadReport> reports = await Future.wait(allReportsFutures); final Iterable<DeviceReloadReport> reports = (await Future.wait(allReportsFutures)).whereType<DeviceReloadReport>();
final vm_service.ReloadReport reloadReport = reports.first.reports[0]; final vm_service.ReloadReport? reloadReport = reports.isEmpty ? null : reports.first.reports[0];
if (!HotRunner.validateReloadReport(reloadReport)) { if (reloadReport == null || !HotRunner.validateReloadReport(reloadReport)) {
// Reload failed. // Reload failed.
HotEvent('reload-reject', HotEvent('reload-reject',
targetPlatform: targetPlatform!, targetPlatform: targetPlatform!,
...@@ -1223,6 +1227,9 @@ Future<OperationResult> _defaultReloadSourcesHelper( ...@@ -1223,6 +1227,9 @@ Future<OperationResult> _defaultReloadSourcesHelper(
// Reset devFS lastCompileTime to ensure the file will still be marked // Reset devFS lastCompileTime to ensure the file will still be marked
// as dirty on subsequent reloads. // as dirty on subsequent reloads.
_resetDevFSCompileTime(flutterDevices); _resetDevFSCompileTime(flutterDevices);
if (reloadReport == null) {
return OperationResult(1, 'No Dart isolates found');
}
final ReloadReportContents contents = ReloadReportContents.fromReloadReport(reloadReport); final ReloadReportContents contents = ReloadReportContents.fromReloadReport(reloadReport);
return OperationResult(1, 'Reload rejected: ${contents.notices.join("\n")}'); return OperationResult(1, 'Reload rejected: ${contents.notices.join("\n")}');
} }
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:test/fake.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../src/context.dart';
void main() {
testUsingContext('defaultReloadSourcesHelper() handles empty DeviceReloadReports)', () {
defaultReloadSourcesHelper(
_FakeHotRunner(),
<FlutterDevice?>[_FakeFlutterDevice()],
false,
const <String, dynamic>{},
'android',
'flutter-sdk',
false,
'test-reason',
);
});
}
class _FakeHotRunner extends Fake implements HotRunner {}
class _FakeDevFS extends Fake implements DevFS {
@override
final Uri? baseUri = Uri();
@override
void resetLastCompiled() {}
}
class _FakeFlutterDevice extends Fake implements FlutterDevice {
@override
final DevFS? devFS = _FakeDevFS();
@override
final FlutterVmService? vmService = _FakeFlutterVmService();
}
class _FakeFlutterVmService extends Fake implements FlutterVmService {
@override
final vm_service.VmService service = _FakeVmService();
}
class _FakeVmService extends Fake implements vm_service.VmService {
@override
Future<_FakeVm> getVM() async => _FakeVm();
}
class _FakeVm extends Fake implements vm_service.VM {
final List<vm_service.IsolateRef> _isolates = <vm_service.IsolateRef>[];
@override
List<vm_service.IsolateRef>? get isolates => _isolates;
}
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