[CP-beta]Fix late variable non-assignment when WASM is enabled (#167987)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

### Issue Link:
What is the link to the issue this cherry-pick is addressing?

https://github.com/flutter/flutter/issues/167887

### Changelog Description:
Explain this cherry pick in one line that is accessible to most Flutter developers. See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples

N/A

### Impact Description:
What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)? Does it impact development (ex. flutter doctor crashes when Android Studio is installed), or the shipping production app (the app crashes on launch)

Tool crashes while running WASM web apps

### Workaround:
Is there a workaround for this issue?

No

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
What are the steps to validate that this fix works?

See attached issue
This commit is contained in:
flutteractionsbot 2025-04-29 07:04:22 -07:00 committed by GitHub
parent 3d03881a90
commit 5cb9ca93ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 123 additions and 31 deletions

View File

@ -435,7 +435,8 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
final String targetPlatform = getNameForTargetPlatform(TargetPlatform.web_javascript);
final String sdkName = await device!.device!.sdkNameAndVersion;
late UpdateFSReport report;
// Will be null if there is no report.
final UpdateFSReport? report;
if (debuggingOptions.buildInfo.isDebug && !debuggingOptions.webUseWasm) {
await runSourceGenerators();
// Don't reset the resident compiler for web, since the extra recompile is
@ -469,6 +470,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
return OperationResult(1, 'Failed to recompile application.');
}
} else {
report = null;
try {
final WebBuilder webBuilder = WebBuilder(
logger: _logger,
@ -490,8 +492,9 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
}
}
late Duration reloadDuration;
late Duration reassembleDuration;
// Both will be null when not assigned.
Duration? reloadDuration;
Duration? reassembleDuration;
try {
if (!deviceIsDebuggable) {
_logger.printStatus('Recompile complete. Page requires refresh.');
@ -578,12 +581,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
fullRestart: true,
reason: reason,
overallTimeInMs: elapsed.inMilliseconds,
syncedBytes: report.syncedBytes,
invalidatedSourcesCount: report.invalidatedSourcesCount,
transferTimeInMs: report.transferDuration.inMilliseconds,
compileTimeInMs: report.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report.scannedSourcesCount,
syncedBytes: report?.syncedBytes,
invalidatedSourcesCount: report?.invalidatedSourcesCount,
transferTimeInMs: report?.transferDuration.inMilliseconds,
compileTimeInMs: report?.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report?.scannedSourcesCount,
).send();
_analytics.send(
Event.hotRunnerInfo(
@ -594,12 +597,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
fullRestart: true,
reason: reason,
overallTimeInMs: elapsed.inMilliseconds,
syncedBytes: report.syncedBytes,
invalidatedSourcesCount: report.invalidatedSourcesCount,
transferTimeInMs: report.transferDuration.inMilliseconds,
compileTimeInMs: report.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report.scannedSourcesCount,
syncedBytes: report?.syncedBytes,
invalidatedSourcesCount: report?.invalidatedSourcesCount,
transferTimeInMs: report?.transferDuration.inMilliseconds,
compileTimeInMs: report?.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report?.scannedSourcesCount,
),
);
} else {
@ -618,14 +621,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
fullRestart: false,
reason: reason,
overallTimeInMs: elapsed.inMilliseconds,
syncedBytes: report.syncedBytes,
invalidatedSourcesCount: report.invalidatedSourcesCount,
transferTimeInMs: report.transferDuration.inMilliseconds,
compileTimeInMs: report.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report.scannedSourcesCount,
reassembleTimeInMs: reassembleDuration.inMilliseconds,
reloadVMTimeInMs: reloadDuration.inMilliseconds,
syncedBytes: report?.syncedBytes,
invalidatedSourcesCount: report?.invalidatedSourcesCount,
transferTimeInMs: report?.transferDuration.inMilliseconds,
compileTimeInMs: report?.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report?.scannedSourcesCount,
reassembleTimeInMs: reassembleDuration?.inMilliseconds,
reloadVMTimeInMs: reloadDuration?.inMilliseconds,
).send();
_analytics.send(
Event.hotRunnerInfo(
@ -636,14 +639,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
fullRestart: false,
reason: reason,
overallTimeInMs: elapsed.inMilliseconds,
syncedBytes: report.syncedBytes,
invalidatedSourcesCount: report.invalidatedSourcesCount,
transferTimeInMs: report.transferDuration.inMilliseconds,
compileTimeInMs: report.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report.scannedSourcesCount,
reassembleTimeInMs: reassembleDuration.inMilliseconds,
reloadVMTimeInMs: reloadDuration.inMilliseconds,
syncedBytes: report?.syncedBytes,
invalidatedSourcesCount: report?.invalidatedSourcesCount,
transferTimeInMs: report?.transferDuration.inMilliseconds,
compileTimeInMs: report?.compileDuration.inMilliseconds,
findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds,
scannedSourcesCount: report?.scannedSourcesCount,
reassembleTimeInMs: reassembleDuration?.inMilliseconds,
reloadVMTimeInMs: reloadDuration?.inMilliseconds,
),
);
}

View File

@ -17,6 +17,7 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/tools/shader_compiler.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/dart/pub.dart';
@ -46,6 +47,7 @@ import '../src/fake_pub_deps.dart';
import '../src/fake_vm_services.dart';
import '../src/fakes.dart' as test_fakes;
import '../src/package_config.dart';
import '../src/test_build_system.dart';
const List<VmServiceExpectation> kAttachLogExpectations = <VmServiceExpectation>[
FakeVmServiceRequest(method: 'streamListen', args: <String, Object>{'streamId': 'Stdout'}),
@ -914,6 +916,88 @@ name: my_app
},
);
// Regression test for https://github.com/flutter/flutter/issues/167887.
testUsingContext(
'WASM builds report analysis without crashing',
() async {
final BufferLogger logger = BufferLogger.test();
final ResidentRunner residentWebRunner = setUpResidentRunner(
flutterDevice,
logger: logger,
systemClock: SystemClock.fixed(DateTime(2001)),
debuggingOptions: DebuggingOptions.enabled(
const BuildInfo(
BuildMode.debug,
null,
trackWidgetCreation: true,
treeShakeIcons: false,
packageConfigPath: '.dart_tool/package_config.json',
// Hot reload only supported with these flags for now.
extraFrontEndOptions: kDdcLibraryBundleFlags,
),
webUseWasm: true,
),
);
fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: kReloadSourcesServiceName,
args: <String, Object>{'isolateId': ''},
jsonResponse: <String, Object>{'type': 'ReloadReport', 'success': true},
),
const FakeVmServiceRequest(
method: 'ext.flutter.reassemble',
jsonResponse: <String, Object>{'type': 'ReloadReport', 'success': true},
),
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{'streamId': 'Isolate'},
),
],
);
setupMocks();
final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher();
final FakeProcess process = FakeProcess();
final Chromium chrome = Chromium(
1,
chromeConnection,
chromiumLauncher: chromiumLauncher,
process: process,
logger: logger,
);
chromiumLauncher.setInstance(chrome);
flutterDevice.device = GoogleChromeDevice(
fileSystem: fileSystem,
chromiumLauncher: chromiumLauncher,
logger: BufferLogger.test(),
platform: FakePlatform(),
processManager: FakeProcessManager.any(),
);
webDevFS.report = UpdateFSReport(success: true);
final Completer<DebugConnectionInfo> connectionInfoCompleter =
Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter));
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
expect(debugConnectionInfo, isNotNull);
final OperationResult result = await residentWebRunner.restart();
expect(logger.statusText, contains('Reloaded application in'));
expect(result.code, 0);
},
overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
FeatureFlags: enableExplicitPackageDependencies,
Pub: FakePubWithPrimedDeps.new,
},
);
// Hot restart is available with and without the DDC library bundle format.
// Test one extra config where `fullRestart` is false without the DDC library
// bundle format - we should do a hot restart in this case because hot reload
@ -1995,6 +2079,11 @@ class FakeChromeTab extends Fake implements ChromeTab {
class FakeWipConnection extends Fake implements WipConnection {
@override
final WipDebugger debugger = FakeWipDebugger();
@override
Future<WipResponse> sendCommand(String method, [Map<String, dynamic>? params]) async {
return WipResponse(<String, dynamic>{'id': 0, 'result': <String, dynamic>{}});
}
}
/// A test implementation of the [ChromiumLauncher] that launches a fixed instance.