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,
|
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(
|
|||||||
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(
|
|||||||
) 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(
|
|||||||
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(
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
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(
|
|||||||
// 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")}');
|
||||||
}
|
}
|
||||||
|
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