From 5cb9ca93eae4b0ceee9fa8307fd6f9fea578190c Mon Sep 17 00:00:00 2001 From: flutteractionsbot <154381524+flutteractionsbot@users.noreply.github.com> Date: Tue, 29 Apr 2025 07:04:22 -0700 Subject: [PATCH] [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 --- .../lib/src/isolated/resident_web_runner.dart | 65 +++++++------- .../resident_web_runner_test.dart | 89 +++++++++++++++++++ 2 files changed, 123 insertions(+), 31 deletions(-) diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index a4ef4e4df7..55361794ff 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -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, ), ); } diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 93fc2c05ae..03c15c46fa 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -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 kAttachLogExpectations = [ FakeVmServiceRequest(method: 'streamListen', args: {'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: [ + ...kAttachExpectations, + const FakeVmServiceRequest( + method: kReloadSourcesServiceName, + args: {'isolateId': ''}, + jsonResponse: {'type': 'ReloadReport', 'success': true}, + ), + const FakeVmServiceRequest( + method: 'ext.flutter.reassemble', + jsonResponse: {'type': 'ReloadReport', 'success': true}, + ), + const FakeVmServiceRequest( + method: 'streamListen', + args: {'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 connectionInfoCompleter = + Completer(); + 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: { + 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 sendCommand(String method, [Map? params]) async { + return WipResponse({'id': 0, 'result': {}}); + } } /// A test implementation of the [ChromiumLauncher] that launches a fixed instance.