[flutter_tools] add support for enable null safety asserts (#61114)

Enable null safety asserts for web debug mode. This induces runtime asserts at the boundaries between null safe and non-null safe libraries. Adds integration test that validates assertion error is thrown.

#61042
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index 1579b6a..9180a60 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -746,6 +746,7 @@
   await _runWebDebugTest('lib/stack_trace.dart');
   await _runWebDebugTest('lib/web_directory_loading.dart');
   await _runWebDebugTest('test/test.dart');
+  await _runWebDebugTest('lib/null_assert_main.dart', enableNullSafety: true);
   await _runWebDebugTest('lib/null_safe_main.dart', enableNullSafety: true);
   await _runWebDebugTest('lib/web_define_loading.dart',
     additionalArguments: <String>[
diff --git a/dev/integration_tests/web/lib/null_assert_main.dart b/dev/integration_tests/web/lib/null_assert_main.dart
new file mode 100644
index 0000000..134720c
--- /dev/null
+++ b/dev/integration_tests/web/lib/null_assert_main.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// @dart=2.8
+
+import 'null_enabled_api.dart';
+
+void main() {
+  dynamic error;
+  try {
+    // Validate that a generated null assertion is thrown.
+    methodThatAcceptsNonNull(null);
+  } catch (err) {
+    error = err;
+  }
+  if (error is AssertionError) {
+    print('--- TEST SUCCEEDED ---');
+  } else {
+    print('--- TEST FAILED ---');
+  }
+}
diff --git a/dev/integration_tests/web/lib/null_enabled_api.dart b/dev/integration_tests/web/lib/null_enabled_api.dart
new file mode 100644
index 0000000..45723e2
--- /dev/null
+++ b/dev/integration_tests/web/lib/null_enabled_api.dart
@@ -0,0 +1,9 @@
+// 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.
+
+// @dart=2.9
+
+void methodThatAcceptsNonNull(int x) {
+  print(x + 2);
+}
diff --git a/packages/flutter_tools/lib/src/build_runner/devfs_web.dart b/packages/flutter_tools/lib/src/build_runner/devfs_web.dart
index d8d9b4a..d80e672 100644
--- a/packages/flutter_tools/lib/src/build_runner/devfs_web.dart
+++ b/packages/flutter_tools/lib/src/build_runner/devfs_web.dart
@@ -791,6 +791,8 @@
         'main_module.bootstrap.js',
         generateMainModule(
           entrypoint: entrypoint,
+          nullSafety: buildInfo.extraFrontEndOptions
+            ?.contains('--enable-experiment=non-nullable') ?? false,
         ),
       );
       // TODO(jonahwilliams): refactor the asset code in this and the regular devfs to
diff --git a/packages/flutter_tools/lib/src/web/bootstrap.dart b/packages/flutter_tools/lib/src/web/bootstrap.dart
index e0a076b..a065222 100644
--- a/packages/flutter_tools/lib/src/web/bootstrap.dart
+++ b/packages/flutter_tools/lib/src/web/bootstrap.dart
@@ -48,12 +48,18 @@
 /// the file `foo/bar/baz.dart` will generate a property named approximately
 /// `foo__bar__baz`. Rather than attempt to guess, we assume the first property of
 /// this object is the module.
-String generateMainModule({@required String entrypoint}) {
+String generateMainModule({
+  @required String entrypoint,
+  @required bool nullSafety,
+}) {
   return '''/* ENTRYPOINT_EXTENTION_MARKER */
 // Create the main module loaded below.
 define("main_module.bootstrap", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
   dart_sdk.dart.setStartAsyncSynchronously(true);
   dart_sdk._debugger.registerDevtoolsFormatter();
+  if ($nullSafety) {
+    dart_sdk.dart.nonNullAsserts(true);
+  }
 
   // See the generateMainModule doc comment.
   var child = {};
diff --git a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart
index 43a579f..e96de47 100644
--- a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart
@@ -23,9 +23,21 @@
   test('generateMainModule embeds urls correctly', () {
     final String result = generateMainModule(
       entrypoint: 'foo/bar/main.js',
+      nullSafety: false,
     );
     // bootstrap main module has correct defined module.
     expect(result, contains('define("main_module.bootstrap", ["foo/bar/main.js", "dart_sdk"], '
       'function(app, dart_sdk) {'));
   });
+
+  test('generateMainModule includes null safety switches', () {
+    final String result = generateMainModule(
+      entrypoint: 'foo/bar/main.js',
+      nullSafety: true,
+    );
+
+    expect(result, contains(
+'''  if (true) {
+    dart_sdk.dart.nonNullAsserts(true);'''));
+  });
 }