[flutter_releases] Flutter 1.24.0-10.1.pre framework cherrypicks (#70495)

* Update engine hash to 1.24.0-10.1.pre

* [null-safety] implement null-safe autodetection for the web (#70126)

Fixes #69416
Fixes #70121

* [flutter_tools] dont use autodetect enum for web (#70189)

* Update engine hash for build fix and pub DEP bump

Co-authored-by: Jonah Williams <jonahwilliams@google.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index d759ac1..501a81f 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-40b8f8227b9a8f4ab78fa9d115cf135aaa885948
+bd390c0310ed240f5781b55b6eb6d5b9f0e2955f
diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart
index 47c13d6..aff6c7c 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/web.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart
@@ -95,10 +95,10 @@
       logger: environment.logger,
     );
     final FlutterProject flutterProject = FlutterProject.current();
-    final String languageVersion = determineLanguageVersion(
+    final LanguageVersion languageVersion = determineLanguageVersion(
       environment.fileSystem.file(targetFile),
       packageConfig[flutterProject.manifest.appName],
-    ) ?? '';
+    );
 
     // Use the PackageConfig to find the correct package-scheme import path
     // for the user application. If the application has a mix of package-scheme
@@ -122,7 +122,7 @@
       final String generatedImport = packageConfig.toPackageUri(generatedUri)?.toString()
         ?? generatedUri.toString();
       contents = '''
-$languageVersion
+// @dart=${languageVersion.major}.${languageVersion.minor}
 
 import 'dart:ui' as ui;
 
@@ -139,7 +139,7 @@
 ''';
     } else {
       contents = '''
-$languageVersion
+// @dart=${languageVersion.major}.${languageVersion.minor}
 
 import 'dart:ui' as ui;
 
diff --git a/packages/flutter_tools/lib/src/dart/language_version.dart b/packages/flutter_tools/lib/src/dart/language_version.dart
index b3f57a6..c4ef436 100644
--- a/packages/flutter_tools/lib/src/dart/language_version.dart
+++ b/packages/flutter_tools/lib/src/dart/language_version.dart
@@ -5,22 +5,24 @@
 import 'package:file/file.dart';
 import 'package:package_config/package_config.dart';
 
-final RegExp _languageVersion = RegExp(r'\/\/\s*@dart');
+final RegExp _languageVersion = RegExp(r'\/\/\s*@dart\s*=\s*([0-9])\.([0-9]+)');
 final RegExp _declarationEnd = RegExp('(import)|(library)|(part)');
 const String _blockCommentStart = '/*';
 const String _blockCommentEnd = '*/';
 
-/// Attempts to read the language version of a dart [file], returning
-/// the entire comment.
+/// The first language version where null safety was available by default.
+final LanguageVersion nullSafeVersion = LanguageVersion(2, 12);
+
+/// Attempts to read the language version of a dart [file].
 ///
 /// If this is not present, falls back to the language version defined in
 /// [package]. If [package] is not provided and there is no
-/// language version header, returns `null`. This does not specifically check
+/// language version header, returns 2.12. This does not specifically check
 /// for language declarations other than library, part, or import.
 ///
 /// The specification for the language version tag is defined at:
 /// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/feature-specification.md#individual-library-language-version-override
-String determineLanguageVersion(File file, Package package) {
+LanguageVersion determineLanguageVersion(File file, Package package) {
   int blockCommentDepth = 0;
   for (final String line in file.readAsLinesSync()) {
     final String trimmedLine = line.trim();
@@ -50,7 +52,17 @@
     // Check for a match with the language version.
     final Match match = _languageVersion.matchAsPrefix(trimmedLine);
     if (match != null) {
-      return trimmedLine;
+      final String rawMajor = match.group(1);
+      final String rawMinor = match.group(2);
+      try {
+        final int major = int.parse(rawMajor);
+        final int minor = int.parse(rawMinor);
+        return LanguageVersion(major, minor);
+      } on FormatException {
+        // Language comment was invalid in a way that the regexp did not
+        // anticipate.
+        break;
+      }
     }
 
     // Check for a declaration which ends the search for a language
@@ -62,7 +74,8 @@
 
   // If the language version cannot be found, use the package version.
   if (package != null) {
-    return '// @dart = ${package.languageVersion}';
+    return package.languageVersion;
   }
-  return null;
+  // Default to 2.12
+  return nullSafeVersion;
 }
diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart
index d19d8e2..76431d3 100644
--- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart
+++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart
@@ -124,7 +124,7 @@
     this.internetAddress,
     this._modules,
     this._digests,
-    this._buildInfo,
+    this._nullSafetyMode,
   ) : basePath = _parseBasePathFromIndexHtml(globals.fs.currentDirectory
             .childDirectory('web')
             .childFile('index.html'));
@@ -169,7 +169,8 @@
     BuildInfo buildInfo,
     bool enableDwds,
     Uri entrypoint,
-    ExpressionCompiler expressionCompiler, {
+    ExpressionCompiler expressionCompiler,
+    NullSafetyMode nullSafetyMode, {
     bool testMode = false,
     DwdsLauncher dwdsLauncher = Dwds.start,
   }) async {
@@ -209,7 +210,7 @@
       address,
       modules,
       digests,
-      buildInfo,
+      nullSafetyMode,
     );
     if (testMode) {
       return server;
@@ -293,7 +294,7 @@
     return server;
   }
 
-  final BuildInfo _buildInfo;
+  final NullSafetyMode _nullSafetyMode;
   final HttpServer _httpServer;
   // If holding these in memory is too much overhead, this can be switched to a
   // RandomAccessFile and read on demand.
@@ -670,12 +671,12 @@
 
   File get _resolveDartSdkJsFile =>
       globals.fs.file(globals.artifacts.getArtifactPath(
-          _dartSdkJsArtifactMap[webRenderer][_buildInfo.nullSafetyMode]
+          _dartSdkJsArtifactMap[webRenderer][_nullSafetyMode]
       ));
 
   File get _resolveDartSdkJsMapFile =>
     globals.fs.file(globals.artifacts.getArtifactPath(
-        _dartSdkJsMapArtifactMap[webRenderer][_buildInfo.nullSafetyMode]
+        _dartSdkJsMapArtifactMap[webRenderer][_nullSafetyMode]
     ));
 
   @override
@@ -733,6 +734,7 @@
     @required this.expressionCompiler,
     @required this.chromiumLauncher,
     @required this.nullAssertions,
+    @required this.nullSafetyMode,
     this.testMode = false,
   }) : _port = port;
 
@@ -749,6 +751,7 @@
   final ChromiumLauncher chromiumLauncher;
   final bool nullAssertions;
   final int _port;
+  final NullSafetyMode nullSafetyMode;
 
   WebAssetServer webAssetServer;
 
@@ -821,6 +824,7 @@
       enableDwds,
       entrypoint,
       expressionCompiler,
+      nullSafetyMode,
       testMode: testMode,
     );
     final int selectedPort = webAssetServer.selectedPort;
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 32f60a4..3ec5c77 100644
--- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
@@ -508,6 +508,7 @@
           expressionCompiler: expressionCompiler,
           chromiumLauncher: _chromiumLauncher,
           nullAssertions: debuggingOptions.nullAssertions,
+          nullSafetyMode: device.nullSafetyMode,
         );
         final Uri url = await device.devFS.create();
         if (debuggingOptions.buildInfo.isDebug) {
@@ -669,12 +670,13 @@
           path: '/' + mainUri.pathSegments.last,
         );
       }
+      final LanguageVersion languageVersion =  determineLanguageVersion(
+        globals.fs.file(mainUri),
+        packageConfig[flutterProject.manifest.appName],
+      );
 
       final String entrypoint = <String>[
-        determineLanguageVersion(
-          globals.fs.file(mainUri),
-          packageConfig[flutterProject.manifest.appName],
-        ),
+        '// @dart=${languageVersion.major}.${languageVersion.minor}',
         '// Flutter web bootstrap script for $importedEntrypoint.',
         '',
         "import 'dart:ui' as ui;",
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index ab841cc..07688c6 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -27,6 +27,8 @@
 import 'bundle.dart';
 import 'cache.dart';
 import 'compile.dart';
+import 'dart/language_version.dart';
+import 'dart/package_map.dart';
 import 'devfs.dart';
 import 'device.dart';
 import 'features.dart';
@@ -46,6 +48,7 @@
     TargetPlatform targetPlatform,
     ResidentCompiler generator,
     this.userIdentifier,
+    this.nullSafetyMode = NullSafetyMode.autodetect,
   }) : assert(buildInfo.trackWidgetCreation != null),
        generator = generator ?? ResidentCompiler(
          globals.artifacts.getArtifactPath(
@@ -81,6 +84,7 @@
     String userIdentifier,
   }) async {
     ResidentCompiler generator;
+    NullSafetyMode nullSafetyMode = buildInfo.nullSafetyMode;
     final TargetPlatform targetPlatform = await device.targetPlatform;
     if (device.platformType == PlatformType.fuchsia) {
       targetModel = TargetModel.flutterRunner;
@@ -101,14 +105,30 @@
         platformDillArtifact = Artifact.webPlatformSoundKernelDill;
         extraFrontEndOptions =  buildInfo.extraFrontEndOptions;
       } else {
-        // TODO(jonahwilliams): null-safe auto detection does not currently
-        // work on the web. Always opt out of null safety if it was not
-        // specifically requested.
-        platformDillArtifact = Artifact.webPlatformKernelDill;
-        extraFrontEndOptions =  <String>[
-          ...?buildInfo.extraFrontEndOptions,
-          '--no-sound-null-safety',
-        ];
+        final PackageConfig packageConfig = await loadPackageConfigWithLogging(
+          globals.fs.file(buildInfo.packagesPath),
+          logger: globals.logger,
+        );
+        final File entrypointFile = globals.fs.file(target);
+        final LanguageVersion languageVersion = determineLanguageVersion(
+          entrypointFile,
+          packageConfig.packageOf(entrypointFile.absolute.uri),
+        );
+        if (languageVersion.major >= nullSafeVersion.major && languageVersion.minor >= nullSafeVersion.minor) {
+          platformDillArtifact = Artifact.webPlatformSoundKernelDill;
+          extraFrontEndOptions =  <String>[
+            ...?buildInfo.extraFrontEndOptions,
+            '--sound-null-safety',
+          ];
+          nullSafetyMode = NullSafetyMode.sound;
+        } else {
+          platformDillArtifact = Artifact.webPlatformKernelDill;
+          extraFrontEndOptions =  <String>[
+            ...?buildInfo.extraFrontEndOptions,
+            '--no-sound-null-safety',
+          ];
+          nullSafetyMode = NullSafetyMode.unsound;
+        }
       }
 
       generator = ResidentCompiler(
@@ -182,6 +202,7 @@
       generator: generator,
       buildInfo: buildInfo,
       userIdentifier: userIdentifier,
+      nullSafetyMode: nullSafetyMode,
     );
   }
 
@@ -189,6 +210,7 @@
   final ResidentCompiler generator;
   final BuildInfo buildInfo;
   final String userIdentifier;
+  final NullSafetyMode nullSafetyMode;
 
   DevFSWriter devFSWriter;
   Stream<Uri> observatoryUris;
diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart
index af86f99..ff713c2 100644
--- a/packages/flutter_tools/lib/src/test/flutter_platform.dart
+++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart
@@ -713,19 +713,19 @@
   }) {
     assert(testUrl.scheme == 'file');
     final File file = globals.fs.file(testUrl);
+    final LanguageVersion languageVersion = determineLanguageVersion(
+      file,
+      _packageConfig[flutterProject?.manifest?.appName],
+    );
     return generateTestBootstrap(
       testUrl: testUrl,
       testConfigFile: findTestConfigFile(globals.fs.file(testUrl)),
       host: host,
       updateGoldens: updateGoldens,
-      languageVersionHeader: determineLanguageVersion(
-        file,
-        _packageConfig[flutterProject?.manifest?.appName],
-      ),
+      languageVersionHeader: '// @dart=${languageVersion.major}.${languageVersion.minor}'
     );
   }
 
-
   File _cachedFontConfig;
 
   @override
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
index 615d0e0..e5202ba 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
@@ -238,7 +238,7 @@
     final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();
 
     // Language version
-    expect(generated, contains('// @dart = 2.7'));
+    expect(generated, contains('// @dart=2.7'));
   }));
 
   test('WebEntrypointTarget generates an entrypoint without plugins and without init platform', () => testbed.run(() async {
diff --git a/packages/flutter_tools/test/general.shard/dart/language_version_test.dart b/packages/flutter_tools/test/general.shard/dart/language_version_test.dart
index 2bfc87d..b9e2ed3 100644
--- a/packages/flutter_tools/test/general.shard/dart/language_version_test.dart
+++ b/packages/flutter_tools/test/general.shard/dart/language_version_test.dart
@@ -19,10 +19,34 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), '// @dart = 2.9');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
   });
 
-  testWithoutContext('detects technically invalid language version', () {
+  testWithoutContext('detects language version in comment without spacing', () {
+    final FileSystem fileSystem = MemoryFileSystem.test();
+    final File file = fileSystem.file('example.dart')
+      ..writeAsStringSync('''
+// Some license
+
+// @dart=2.9
+''');
+
+    expect(determineLanguageVersion(file, null),  LanguageVersion(2, 9));
+  });
+
+  testWithoutContext('detects language version in comment with more numbers', () {
+    final FileSystem fileSystem = MemoryFileSystem.test();
+    final File file = fileSystem.file('example.dart')
+      ..writeAsStringSync('''
+// Some license
+
+// @dart=2.12
+''');
+
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
+  });
+
+  testWithoutContext('does not detect invalid language version', () {
     final FileSystem fileSystem = MemoryFileSystem.test();
     final File file = fileSystem.file('example.dart')
       ..writeAsStringSync('''
@@ -31,7 +55,7 @@
 // @dart
 ''');
 
-    expect(determineLanguageVersion(file, null), '// @dart');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('detects language version with leading whitespace', () {
@@ -43,7 +67,7 @@
     // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), '// @dart = 2.9');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
   });
 
   testWithoutContext('detects language version with tabs', () {
@@ -55,7 +79,7 @@
 //\t@dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), '//\t@dart = 2.9');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
   });
 
   testWithoutContext('detects language version with tons of whitespace', () {
@@ -64,10 +88,10 @@
       ..writeAsStringSync('''
 // Some license
 
-//        @dart       = 23
+//        @dart       = 2.23
 ''');
 
-    expect(determineLanguageVersion(file, null), '//        @dart       = 23');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 23));
   });
 
   testWithoutContext('does not detect language version in dartdoc', () {
@@ -79,7 +103,7 @@
 /// @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version in block comment', () {
@@ -93,7 +117,7 @@
 */
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version in nested block comment', () {
@@ -109,7 +133,7 @@
 */
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('detects language version after nested block comment', () {
@@ -124,7 +148,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), '// @dart = 2.9');
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
   });
 
   testWithoutContext('does not crash with unbalanced opening block comments', () {
@@ -139,7 +163,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not crash with unbalanced closing block comments', () {
@@ -154,7 +178,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version in single line block comment', () {
@@ -166,7 +190,7 @@
 /* // @dart = 2.9 */
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version after import declaration', () {
@@ -180,7 +204,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version after part declaration', () {
@@ -194,7 +218,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('does not detect language version after library declaration', () {
@@ -208,7 +232,7 @@
 // @dart = 2.9
 ''');
 
-    expect(determineLanguageVersion(file, null), null);
+    expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
   });
 
   testWithoutContext('looks up language version from package if not found in file', () {
@@ -223,6 +247,6 @@
       languageVersion: LanguageVersion(2, 7),
     );
 
-    expect(determineLanguageVersion(file, package), '// @dart = 2.7');
+    expect(determineLanguageVersion(file, package), LanguageVersion(2, 7));
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
index ed77f83..c90af3d 100644
--- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
@@ -629,6 +629,7 @@
       testMode: true,
       expressionCompiler: null,
       chromiumLauncher: null,
+      nullSafetyMode: NullSafetyMode.unsound,
     );
     webDevFS.requireJS.createSync(recursive: true);
     webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -745,6 +746,7 @@
       testMode: true,
       expressionCompiler: null,
       chromiumLauncher: null,
+      nullSafetyMode: NullSafetyMode.autodetect,
     );
     webDevFS.requireJS.createSync(recursive: true);
     webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -853,6 +855,7 @@
       expressionCompiler: null,
       chromiumLauncher: null,
       nullAssertions: true,
+      nullSafetyMode: NullSafetyMode.sound,
     );
     webDevFS.requireJS.createSync(recursive: true);
     webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -903,6 +906,7 @@
       testMode: true,
       expressionCompiler: null,
       chromiumLauncher: null,
+      nullSafetyMode: NullSafetyMode.sound,
     );
     webDevFS.requireJS.createSync(recursive: true);
     webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -954,6 +958,7 @@
       testMode: true,
       expressionCompiler: null,
       chromiumLauncher: null,
+      nullSafetyMode: NullSafetyMode.sound,
     );
     webDevFS.requireJS.createSync(recursive: true);
     webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -981,6 +986,7 @@
       false,
       Uri.base,
       null,
+      null,
       testMode: true);
 
     expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null);