Unverified Commit 9e5c8771 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] wire up native-null-assertions for flutter web (#71618)

parent 088b7c36
......@@ -39,6 +39,9 @@ const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
/// Whether the dart2js build should output source maps.
const String kSourceMapsEnabled = 'SourceMaps';
/// Whether the dart2js native null assertions are enabled.
const String kNativeNullAssertions = 'NativeNullAssertions';
/// The caching strategy for the generated service worker.
enum ServiceWorkerStrategy {
/// Download the app shell eagerly and all other assets lazily.
......@@ -190,6 +193,7 @@ class Dart2JSTarget extends Target {
Future<void> build(Environment environment) async {
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final bool sourceMapsEnabled = environment.defines[kSourceMapsEnabled] == 'true';
final bool nativeNullAssertions = environment.defines[kNativeNullAssertions] == 'true';
final List<String> sharedCommandOptions = <String>[
globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
......@@ -197,6 +201,8 @@ class Dart2JSTarget extends Target {
globals.artifacts.getArtifactPath(Artifact.dart2jsSnapshot),
'--libraries-spec=${globals.fs.path.join(globals.artifacts.getArtifactPath(Artifact.flutterWebSdk), 'libraries.json')}',
...?decodeDartDefines(environment.defines, kExtraFrontEndOptions),
if (nativeNullAssertions)
'--native-null-assertions',
if (buildMode == BuildMode.profile)
'-Ddart.vm.profile=true'
else
......
......@@ -26,6 +26,7 @@ class BuildWebCommand extends BuildSubCommand {
usesWebRendererOption();
addEnableExperimentation(hide: !verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp);
addNativeNullAssertions(hide: false);
argParser.addFlag('csp',
defaultsTo: false,
negatable: false,
......@@ -90,6 +91,7 @@ class BuildWebCommand extends BuildSubCommand {
boolArg('csp'),
stringArg('pwa-strategy'),
boolArg('source-maps'),
boolArg('native-null-assertions'),
);
return FlutterCommandResult.success();
}
......
......@@ -30,6 +30,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDartDefineOption();
usesFlavorOption();
usesWebRendererOption();
addNativeNullAssertions(hide: !verboseHelp);
argParser
..addFlag('trace-startup',
negatable: false,
......@@ -205,6 +206,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
&& boolArg('fast-start')
&& !runningWithPrebuiltApplication,
nullAssertions: boolArg('null-assertions'),
nativeNullAssertions: boolArg('native-null-assertions'),
);
}
}
......
......@@ -863,6 +863,7 @@ class DebuggingOptions {
this.vmserviceOutFile,
this.fastStart = false,
this.nullAssertions = false,
this.nativeNullAssertions = false,
}) : debuggingEnabled = true;
DebuggingOptions.disabled(this.buildInfo, {
......@@ -897,7 +898,8 @@ class DebuggingOptions {
vmserviceOutFile = null,
fastStart = false,
webEnableExpressionEvaluation = false,
nullAssertions = false;
nullAssertions = false,
nativeNullAssertions = false;
final bool debuggingEnabled;
......@@ -947,6 +949,12 @@ class DebuggingOptions {
final bool nullAssertions;
/// Additional null runtime checks inserted for web applications.
///
/// See also:
/// * https://github.com/dart-lang/sdk/blob/master/sdk/lib/html/doc/NATIVE_NULL_ASSERTIONS.md
final bool nativeNullAssertions;
bool get hasObservatoryPort => hostVmServicePort != null;
}
......
......@@ -605,6 +605,7 @@ class WebDevFS implements DevFS {
@required this.expressionCompiler,
@required this.chromiumLauncher,
@required this.nullAssertions,
@required this.nativeNullAssertions,
@required this.nullSafetyMode,
this.testMode = false,
}) : _port = port;
......@@ -621,6 +622,7 @@ class WebDevFS implements DevFS {
final ExpressionCompiler expressionCompiler;
final ChromiumLauncher chromiumLauncher;
final bool nullAssertions;
final bool nativeNullAssertions;
final int _port;
final NullSafetyMode nullSafetyMode;
......@@ -779,6 +781,7 @@ class WebDevFS implements DevFS {
generateMainModule(
entrypoint: entrypoint,
nullAssertions: nullAssertions,
nativeNullAssertions: nativeNullAssertions,
),
);
// TODO(jonahwilliams): refactor the asset code in this and the regular devfs to
......
......@@ -501,6 +501,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
chromiumLauncher: _chromiumLauncher,
nullAssertions: debuggingOptions.nullAssertions,
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
nativeNullAssertions: debuggingOptions.nativeNullAssertions,
);
final Uri url = await device.devFS.create();
if (debuggingOptions.buildInfo.isDebug) {
......@@ -520,6 +521,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
false,
kNoneWorker,
true,
debuggingOptions.nativeNullAssertions,
);
}
await device.device.startApp(
......@@ -587,6 +589,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
false,
kNoneWorker,
true,
debuggingOptions.nativeNullAssertions,
);
} on ToolExit {
return OperationResult(1, 'Failed to recompile application.');
......
......@@ -674,6 +674,19 @@ abstract class FlutterCommand extends Command<void> {
);
}
void addNativeNullAssertions({ bool hide = false }) {
argParser.addFlag('native-null-assertions',
defaultsTo: true,
hide: hide,
help: 'Enables additional runtime null checks in web applications to ensure '
'the correct nullability of native (such as in dart:html) and external '
'(such as with JS interop) types. This is enabled by default but only takes '
'effect in sound mode. To report an issue with a null assertion failure in '
'dart:html or the other dart web libraries, please file a bug at '
'https://github.com/dart-lang/sdk/issues/labels/web-libraries .'
);
}
/// Adds build options common to all of the desktop build commands.
void addCommonDesktopBuildOptions({ bool verboseHelp = false }) {
addBuildModeFlags(verboseHelp: verboseHelp);
......
......@@ -224,6 +224,7 @@ class FlutterWebPlatform extends PlatformPlugin {
final String generatedFile = _fileSystem.path.split(leadingPath).join('_') + '.dart.test.dart.js';
return shelf.Response.ok(generateMainModule(
nullAssertions: nullAssertions,
nativeNullAssertions: true,
bootstrapModule: _fileSystem.path.basename(leadingPath) + '.dart.bootstrap',
entrypoint: '/' + generatedFile
), headers: <String, String>{
......
......@@ -54,6 +54,7 @@ document.head.appendChild(requireEl);
String generateMainModule({
@required String entrypoint,
@required bool nullAssertions,
@required bool nativeNullAssertions,
String bootstrapModule = 'main_module.bootstrap',
}) {
// TODO(jonahwilliams): fix typo in dwds and update.
......@@ -63,9 +64,8 @@ String generateMainModule({
define("$bootstrapModule", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
dart_sdk.dart.setStartAsyncSynchronously(true);
dart_sdk._debugger.registerDevtoolsFormatter();
if ($nullAssertions) {
dart_sdk.dart.nonNullAsserts(true);
}
dart_sdk.dart.nonNullAsserts($nullAssertions);
dart_sdk.dart.nativeNonNullAsserts($nativeNullAssertions);
// See the generateMainModule doc comment.
var child = {};
......
......@@ -24,6 +24,7 @@ Future<void> buildWeb(
bool csp,
String serviceWorkerStrategy,
bool sourceMaps,
bool nativeNullAssertions,
) async {
if (!flutterProject.web.existsSync()) {
throwToolExit('Missing index.html.');
......@@ -51,6 +52,7 @@ Future<void> buildWeb(
kCspMode: csp.toString(),
kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
kSourceMapsEnabled: sourceMaps.toString(),
kNativeNullAssertions: nativeNullAssertions.toString(),
if (serviceWorkerStrategy != null)
kServiceWorkerStrategy: serviceWorkerStrategy,
if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false)
......
......@@ -56,6 +56,7 @@ void main() {
false,
null,
true,
true,
), throwsToolExit());
}, overrides: <Type, Generator>{
Platform: () => fakePlatform,
......
......@@ -392,6 +392,40 @@ void main() {
ProcessManager: () => processManager,
}));
test('Dart2JSTarget calls dart2js with expected args in release mode with native null assertions', () => testbed.run(() async {
environment.defines[kBuildMode] = 'release';
environment.defines[kNativeNullAssertions] = 'true';
processManager.addCommand(FakeCommand(
command: <String>[
...kDart2jsLinuxArgs,
'--native-null-assertions',
'-Ddart.vm.product=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
'--packages=.packages',
'--cfe-only',
environment.buildDir.childFile('main.dart').absolute.path,
]
));
processManager.addCommand(FakeCommand(
command: <String>[
...kDart2jsLinuxArgs,
'--native-null-assertions',
'-Ddart.vm.product=true',
'--no-source-maps',
'-O4',
'-o',
environment.buildDir.childFile('main.dart.js').absolute.path,
environment.buildDir.childFile('app.dill').absolute.path,
]
));
await const Dart2JSTarget().build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
test('Dart2JSTarget calls dart2js with expected args in release with dart2js optimization override', () => testbed.run(() async {
environment.defines[kBuildMode] = 'release';
environment.defines[kDart2jsOptimization] = 'O3';
......
......@@ -24,6 +24,7 @@ void main() {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nullAssertions: false,
nativeNullAssertions: false,
);
// bootstrap main module has correct defined module.
expect(result, contains('define("main_module.bootstrap", ["foo/bar/main.js", "dart_sdk"], '
......@@ -34,6 +35,7 @@ void main() {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nullAssertions: false,
nativeNullAssertions: false,
bootstrapModule: 'foo_module.bootstrap',
);
// bootstrap main module has correct defined module.
......@@ -45,11 +47,22 @@ void main() {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nullAssertions: true,
nativeNullAssertions: true,
);
expect(result, contains('''
if (true) {
dart_sdk.dart.nonNullAsserts(true);'''));
expect(result, contains('''dart_sdk.dart.nonNullAsserts(true);'''));
expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(true);'''));
});
test('generateMainModule can disable null safety switches', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nullAssertions: false,
nativeNullAssertions: false,
);
expect(result, contains('''dart_sdk.dart.nonNullAsserts(false);'''));
expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(false);'''));
});
test('generateTestBootstrapFileContents embeds urls correctly', () {
......
......@@ -617,6 +617,7 @@ void main() {
useSseForDebugProxy: true,
useSseForDebugBackend: true,
nullAssertions: true,
nativeNullAssertions: true,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
......@@ -732,6 +733,7 @@ void main() {
useSseForDebugProxy: true,
useSseForDebugBackend: true,
nullAssertions: true,
nativeNullAssertions: true,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
......@@ -850,6 +852,7 @@ void main() {
expressionCompiler: null,
chromiumLauncher: null,
nullAssertions: true,
nativeNullAssertions: true,
nullSafetyMode: NullSafetyMode.sound,
);
webDevFS.requireJS.createSync(recursive: true);
......@@ -886,6 +889,7 @@ void main() {
useSseForDebugProxy: true,
useSseForDebugBackend: true,
nullAssertions: true,
nativeNullAssertions: true,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
......@@ -936,6 +940,7 @@ void main() {
useSseForDebugProxy: true,
useSseForDebugBackend: true,
nullAssertions: true,
nativeNullAssertions: true,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment