[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);'''));
+ });
}