[ddc] Add option to throw null safety violations

Allows sound-like null safety when running mixed applications in weak
mode.

This is not a specified option and is only intended to assist large
scale migration efforts.

Change-Id: Icd0abb0e876d16e719a01e8381eef55a2b511051
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/171821
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
Nicholas Shahan 2020-11-16 22:58:50 +00:00 committed by commit-bot@chromium.org
parent 7878baf1eb
commit bfe8aa8d5b
5 changed files with 98 additions and 49 deletions

View file

@ -49,18 +49,18 @@ void main(List<String> args) async {
..addFlag('sound-null-safety',
help: 'Compile for sound null safety at runtime. Passed through to the '
'DDC binary. Defaults to false.',
defaultsTo: false,
negatable: true)
defaultsTo: false)
..addFlag('null-assertions',
help: 'Run with assertions that values passed to non-nullable method '
'parameters are not null.',
defaultsTo: false,
negatable: true)
defaultsTo: false)
..addFlag('native-null-assertions',
help: 'Run with assertions on non-nullable values returned from native '
'APIs.',
defaultsTo: true,
negatable: true)
..addFlag('weak-null-safety-errors',
help: 'Treat weak null safety warnings as errors.', defaultsTo: false)
..addFlag('observe',
help:
'Run the compiler in the Dart VM with --observe. Implies --debug.',
@ -86,7 +86,6 @@ void main(List<String> args) async {
abbr: 'v',
help: 'Echos the commands, arguments, and environment this script is '
'running.',
negatable: false,
defaultsTo: false)
..addOption('vm-service-port',
help: 'Specify the observatory port. Implied --observe.');
@ -115,19 +114,10 @@ void main(List<String> args) async {
var compile = mode == 'compile' || mode == 'all';
var run = mode == 'run' || mode == 'all';
var verbose = options['verbose'] as bool;
var soundNullSafety = options['sound-null-safety'] as bool;
var nonNullAsserts = options['null-assertions'] as bool;
var nativeNonNullAsserts = options['null-assertions'] as bool;
var soundNullSafety = options['sound-null-safety'] as bool;
// Enable null safety either by passing the `non-nullable` experiment flag or
// `sound-null-safety`.
var nnbd = experiments.contains('non-nullable') || soundNullSafety;
// Ensure non-nullable is passed as a flag.
if (soundNullSafety && !experiments.contains('non-nullable')) {
experiments.add('non-nullable');
}
var weakNullSafetyErrors = options['weak-null-safety-errors'] as bool;
var entry = p.canonicalize(options.rest.first);
var out = (options['out'] as String) ?? p.setExtension(entry, '.js');
var libRoot = p.dirname(entry);
@ -275,11 +265,12 @@ void main(List<String> args) async {
require(['dart_sdk', '$basename'],
function(sdk, app) {
'use strict';
if ($nnbd) {
sdk.dart.weakNullSafetyWarnings(!$soundNullSafety);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
}
sdk.dart.weakNullSafetyWarnings(
!($weakNullSafetyErrors || $soundNullSafety));
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
sdk._debugger.registerDevtoolsFormatter();
app.$libname.main([]);
});
@ -311,11 +302,11 @@ let sdk = require(\"dart_sdk\");
sdk.dart.global.self = sdk.dart.global;
let main = require(\"./$basename\").$libname.main;
try {
if ($nnbd) {
sdk.dart.weakNullSafetyWarnings(!$soundNullSafety);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
}
sdk.dart.weakNullSafetyWarnings(
!($weakNullSafetyErrors || $soundNullSafety));
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
sdk._isolate_helper.startRootIsolate(main, []);
} catch(e) {
if (!source_maps) {
@ -346,14 +337,14 @@ load("$ddcPath/lib/js/legacy/dart_library.js");
load("$sdkJsPath/dart_sdk.js");
load("$out");
let dart_sdk = dart_library.import('dart_sdk');
let sdk = dart_library.import('dart_sdk');
// Create a self reference for JS interop tests that set fields on self.
dart_sdk.dart.global.self = dart_sdk.dart.global;
if ($nnbd) {
dart_sdk.dart.weakNullSafetyWarnings(!$soundNullSafety);
dart_sdk.dart.nonNullAsserts($nonNullAsserts);
dart_sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
}
sdk.dart.global.self = sdk.dart.global;
sdk.dart.weakNullSafetyWarnings(
!($weakNullSafetyErrors || $soundNullSafety));
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
dart_library.start('$basename', '$libname');
''';
var d8File = p.setExtension(out, '.d8.js');

View file

@ -147,9 +147,16 @@ bool _invalidVariableName(String keyword, {bool strictMode = true}) {
/// [testJSDir] is the relative path to the build directory where the
/// dartdevc-generated JS file is stored. [nonNullAsserts] enables non-null
/// assertions for non-nullable method parameters when running with weak null
/// safety.
String dartdevcHtml(String testName, String testNameAlias, String testJSDir,
Compiler compiler, NnbdMode mode, bool nonNullAsserts) {
/// safety. [weakNullSafetyErrors] enables null safety type violations to throw
/// when running in weak mode.
String dartdevcHtml(
String testName,
String testNameAlias,
String testJSDir,
Compiler compiler,
NnbdMode mode,
bool nonNullAsserts,
bool weakNullSafetyErrors) {
var testId = pathToJSIdentifier(testName);
var testIdAlias = pathToJSIdentifier(testNameAlias);
var isNnbd = mode != NnbdMode.legacy;
@ -233,7 +240,8 @@ requirejs(["$testName", "dart_sdk", "async_helper"],
};
if ($isNnbd) {
sdk.dart.weakNullSafetyWarnings(!$isNnbdStrong);
sdk.dart.weakNullSafetyWarnings(!($weakNullSafetyErrors || $isNnbdStrong));
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
sdk.dart.nonNullAsserts($nonNullAsserts);
}

View file

@ -925,8 +925,16 @@ class StandardTestSuite extends TestSuite {
Path(compilationTempDir).relativeTo(Repository.dir).toString();
var nullAssertions =
testFile.sharedOptions.contains('--null-assertions');
content = dartdevcHtml(nameNoExt, nameFromModuleRootNoExt, jsDir,
configuration.compiler, configuration.nnbdMode, nullAssertions);
var weakNullSafetyErrors =
testFile.ddcOptions.contains('--weak-null-safety-errors');
content = dartdevcHtml(
nameNoExt,
nameFromModuleRootNoExt,
jsDir,
configuration.compiler,
configuration.nnbdMode,
nullAssertions,
weakNullSafetyErrors);
}
}

View file

@ -12,26 +12,49 @@ part of dart._runtime;
@notNull
external bool compileTimeFlag(String flag);
_throwNullSafetyWarningError() => throw UnsupportedError(
'Null safety errors cannot be shown as warnings when running with sound '
'null safety.');
_throwInvalidFlagError(String message) =>
throw UnsupportedError('Invalid flag combination.\n$message');
@notNull
bool _weakNullSafetyWarnings = false;
/// Sets the runtime mode to show warnings when running with weak null safety.
/// Sets the runtime mode to show warnings when types violate sound null safety.
///
/// These are warnings for issues that will become errors when sound null safety
/// is enabled. Showing warnings while running with sound null safety is not
/// supported (they will be errors).
/// This option is not compatible with weak null safety errors or sound null
/// safety (the warnings will be errors).
void weakNullSafetyWarnings(bool showWarnings) {
if (showWarnings && compileTimeFlag('soundNullSafety')) {
_throwNullSafetyWarningError();
_throwInvalidFlagError(
'Null safety violations cannot be shown as warnings when running with '
'sound null safety.');
}
_weakNullSafetyWarnings = showWarnings;
}
@notNull
bool _weakNullSafetyErrors = false;
/// Sets the runtime mode to throw errors when types violate sound null safety.
///
/// This option is not compatible with weak null safety warnings (the warnings
/// are now errors) or sound null safety (the errors are already errors).
void weakNullSafetyErrors(bool showErrors) {
if (showErrors && compileTimeFlag('soundNullSafety')) {
_throwInvalidFlagError(
'Null safety violations are already thrown as errors when running with '
'sound null safety.');
}
if (showErrors && _weakNullSafetyWarnings) {
_throwInvalidFlagError(
'Null safety violations can be shown as warnings or thrown as errors, '
'not both.');
}
_weakNullSafetyErrors = showErrors;
}
@notNull
bool _nonNullAsserts = false;
@ -250,10 +273,12 @@ void _warn(arg) {
JS('void', 'console.warn(#)', arg);
}
void _nullWarn(arg) {
void _nullWarn(message) {
if (_weakNullSafetyWarnings) {
_warn('$arg\n'
_warn('$message\n'
'This will become a failure when runtime null safety is enabled.');
} else if (_weakNullSafetyErrors) {
throw TypeErrorImpl(message);
}
}

View file

@ -0,0 +1,17 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Requirements=nnbd-weak
// dartdevcOptions=--weak-null-safety-errors
import 'package:expect/expect.dart';
void main() {
Expect.throwsTypeError(() => null as int);
dynamic dynamicNull = null;
Expect.throwsTypeError(() => fn(dynamicNull));
}
void fn(StringBuffer arg) {}