Fix StateError during hot reload when no Dart isolates found (#130537)
Fixes https://github.com/flutter/flutter/issues/116262
This commit is contained in:
parent
f5a5581fdb
commit
b91aedb175
@ -91,7 +91,7 @@ class HotRunner extends ResidentRunner {
|
||||
this.multidexEnabled = false,
|
||||
super.devtoolsHandler,
|
||||
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
|
||||
ReloadSourcesHelper reloadSourcesHelper = _defaultReloadSourcesHelper,
|
||||
ReloadSourcesHelper reloadSourcesHelper = defaultReloadSourcesHelper,
|
||||
ReassembleHelper reassembleHelper = _defaultReassembleHelper,
|
||||
}) : _stopwatchFactory = stopwatchFactory,
|
||||
_reloadSourcesHelper = reloadSourcesHelper,
|
||||
@ -1174,7 +1174,8 @@ typedef ReloadSourcesHelper = Future<OperationResult> Function(
|
||||
String? reason,
|
||||
);
|
||||
|
||||
Future<OperationResult> _defaultReloadSourcesHelper(
|
||||
@visibleForTesting
|
||||
Future<OperationResult> defaultReloadSourcesHelper(
|
||||
HotRunner hotRunner,
|
||||
List<FlutterDevice?> flutterDevices,
|
||||
bool? pause,
|
||||
@ -1186,7 +1187,7 @@ Future<OperationResult> _defaultReloadSourcesHelper(
|
||||
) async {
|
||||
final Stopwatch vmReloadTimer = Stopwatch()..start();
|
||||
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) {
|
||||
final List<Future<vm_service.ReloadReport>> reportFutures = await _reloadDeviceSources(
|
||||
@ -1194,10 +1195,13 @@ Future<OperationResult> _defaultReloadSourcesHelper(
|
||||
entryPath,
|
||||
pause: pause,
|
||||
);
|
||||
allReportsFutures.add(Future.wait(reportFutures).then(
|
||||
allReportsFutures.add(Future.wait(reportFutures).then<DeviceReloadReport?>(
|
||||
(List<vm_service.ReloadReport> reports) async {
|
||||
// TODO(aam): Investigate why we are validating only first reload report,
|
||||
// which seems to be current behavior
|
||||
if (reports.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final vm_service.ReloadReport firstReport = reports.first;
|
||||
// Don't print errors because they will be printed further down when
|
||||
// `validateReloadReport` is called again.
|
||||
@ -1208,9 +1212,9 @@ Future<OperationResult> _defaultReloadSourcesHelper(
|
||||
},
|
||||
));
|
||||
}
|
||||
final List<DeviceReloadReport> reports = await Future.wait(allReportsFutures);
|
||||
final vm_service.ReloadReport reloadReport = reports.first.reports[0];
|
||||
if (!HotRunner.validateReloadReport(reloadReport)) {
|
||||
final Iterable<DeviceReloadReport> reports = (await Future.wait(allReportsFutures)).whereType<DeviceReloadReport>();
|
||||
final vm_service.ReloadReport? reloadReport = reports.isEmpty ? null : reports.first.reports[0];
|
||||
if (reloadReport == null || !HotRunner.validateReloadReport(reloadReport)) {
|
||||
// Reload failed.
|
||||
HotEvent('reload-reject',
|
||||
targetPlatform: targetPlatform!,
|
||||
@ -1223,6 +1227,9 @@ Future<OperationResult> _defaultReloadSourcesHelper(
|
||||
// Reset devFS lastCompileTime to ensure the file will still be marked
|
||||
// as dirty on subsequent reloads.
|
||||
_resetDevFSCompileTime(flutterDevices);
|
||||
if (reloadReport == null) {
|
||||
return OperationResult(1, 'No Dart isolates found');
|
||||
}
|
||||
final ReloadReportContents contents = ReloadReportContents.fromReloadReport(reloadReport);
|
||||
return OperationResult(1, 'Reload rejected: ${contents.notices.join("\n")}');
|
||||
}
|
||||
|
62
packages/flutter_tools/test/general.shard/run_hot_test.dart
Normal file
62
packages/flutter_tools/test/general.shard/run_hot_test.dart
Normal file
@ -0,0 +1,62 @@
|
||||
// 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user