Version 2.11.0-248.0.dev Merge commit '1c1fef8d682e59e4e92eba0c01cf75fe3b15af5a' into 'dev'
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart index 1c52968..b042613 100644 --- a/pkg/compiler/lib/src/commandline_options.dart +++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -23,7 +23,6 @@ static const String enableCheckedMode = '--enable-checked-mode'; static const String enableAsserts = '--enable-asserts'; static const String enableNullAssertions = '--null-assertions'; - static const String enableNativeNullAssertions = '--native-null-assertions'; static const String enableDiagnosticColors = '--enable-diagnostic-colors'; static const String experimentalTrackAllocations = '--experimental-track-allocations'; @@ -76,6 +75,10 @@ '--no-frequency-based-minification'; // Disables minification even if enabled by other options, e.g. '-O2'. static const String noMinify = '--no-minify'; + + static const String nativeNullAssertions = '--native-null-assertions'; + static const String noNativeNullAssertions = '--no-native-null-assertions'; + static const String noSourceMaps = '--no-source-maps'; static const String preserveUris = '--preserve-uris'; static const String printLegacyStars = '--debug-print-legacy-stars';
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart index 3bd43ed..7142b49 100644 --- a/pkg/compiler/lib/src/dart2js.dart +++ b/pkg/compiler/lib/src/dart2js.dart
@@ -444,7 +444,8 @@ (_) => setCheckedMode(Flags.enableCheckedMode)), new OptionHandler(Flags.enableAsserts, passThrough), new OptionHandler(Flags.enableNullAssertions, passThrough), - new OptionHandler(Flags.enableNativeNullAssertions, passThrough), + new OptionHandler(Flags.nativeNullAssertions, passThrough), + new OptionHandler(Flags.noNativeNullAssertions, passThrough), new OptionHandler(Flags.trustTypeAnnotations, setTrustTypeAnnotations), new OptionHandler(Flags.trustPrimitives, passThrough), new OptionHandler(Flags.trustJSInteropTypeAnnotations, ignoreOption),
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart index 15d7550..437a842 100644 --- a/pkg/compiler/lib/src/ir/util.dart +++ b/pkg/compiler/lib/src/ir/util.dart
@@ -242,3 +242,29 @@ /// and function type variables) are considered. bool containsFreeVariables(ir.DartType type) => type.accept(const _FreeVariableVisitor()); + +/// Returns true if [importUri] corresponds to dart:html and related libraries. +bool _isWebLibrary(Uri importUri) => + importUri.scheme == 'dart' && + (importUri.path == 'html' || + importUri.path == 'svg' || + importUri.path == 'indexed_db' || + importUri.path == 'web_audio' || + importUri.path == 'web_gl' || + importUri.path == 'web_sql' || + importUri.path == 'html_common') || + // Mock web library path for testing. + importUri.path + .contains('native_null_assertions/web_library_interfaces.dart'); + +bool nodeIsInWebLibrary(ir.TreeNode node) { + if (node == null) return false; + if (node is ir.Library) return _isWebLibrary(node.importUri); + return nodeIsInWebLibrary(node.parent); +} + +bool memberEntityIsInWebLibrary(MemberEntity entity) { + var importUri = entity?.library?.canonicalUri; + if (importUri == null) return false; + return _isWebLibrary(importUri); +}
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart index c0638c2..e999cd9 100644 --- a/pkg/compiler/lib/src/options.dart +++ b/pkg/compiler/lib/src/options.dart
@@ -240,7 +240,8 @@ /// Whether to generate code asserting that non-nullable return values of /// `@Native` methods or `JS()` invocations are checked for being non-null. - bool enableNativeNullAssertions = false; + bool nativeNullAssertions = false; + bool _noNativeNullAssertions = false; /// Whether to generate a source-map file together with the output program. bool generateSourceMap = true; @@ -454,8 +455,9 @@ _hasOption(options, Flags.enableAsserts) ..enableNullAssertions = _hasOption(options, Flags.enableCheckedMode) || _hasOption(options, Flags.enableNullAssertions) - ..enableNativeNullAssertions = - _hasOption(options, Flags.enableNativeNullAssertions) + ..nativeNullAssertions = _hasOption(options, Flags.nativeNullAssertions) + .._noNativeNullAssertions = + _hasOption(options, Flags.noNativeNullAssertions) ..experimentalTrackAllocations = _hasOption(options, Flags.experimentalTrackAllocations) ..experimentalAllocationsPath = _extractStringOption( @@ -534,6 +536,10 @@ throw ArgumentError("'${Flags.soundNullSafety}' requires the " "'non-nullable' experiment to be enabled"); } + if (nativeNullAssertions && _noNativeNullAssertions) { + throw ArgumentError("'${Flags.nativeNullAssertions}' incompatible with " + "'${Flags.noNativeNullAssertions}'"); + } } void deriveOptions() { @@ -592,6 +598,8 @@ if (_disableMinification) { enableMinification = false; } + + if (_noNativeNullAssertions) nativeNullAssertions = false; } /// Returns `true` if warnings and hints are shown for all packages.
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart index 619683d..3e327fb 100644 --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1545,10 +1545,11 @@ sourceInformation: null)); HInstruction value = pop(); // TODO(johnniwinther): Provide source information. - if (options.enableNativeNullAssertions) { + if (options.nativeNullAssertions) { if (_isNonNullableByDefault(functionNode)) { DartType type = _getDartTypeIfValid(functionNode.returnType); - if (dartTypes.isNonNullableIfSound(type)) { + if (dartTypes.isNonNullableIfSound(type) && + nodeIsInWebLibrary(functionNode)) { push(HNullCheck(value, _abstractValueDomain.excludeNull(returnType), sticky: true)); value = pop(); @@ -4799,8 +4800,8 @@ /// If [invocation] is a `JS()` invocation in a web library and the static /// type is non-nullable, add a check to make sure it isn't null. void _maybeAddNullCheckOnJS(ir.StaticInvocation invocation) { - if (options.enableNativeNullAssertions && - _isInWebLibrary(invocation) && + if (options.nativeNullAssertions && + nodeIsInWebLibrary(invocation) && closedWorld.dartTypes .isNonNullableIfSound(_getStaticType(invocation).type)) { HInstruction code = pop(); @@ -4810,25 +4811,6 @@ } } - /// Returns true if [node] belongs to dart:html and related libraries. - bool _isInWebLibrary(ir.TreeNode node) { - if (node == null) return false; - bool isWebLibrary(Uri importUri) => - importUri.scheme == 'dart' && - (importUri.path == 'html' || - importUri.path == 'svg' || - importUri.path == 'indexed_db' || - importUri.path == 'web_audio' || - importUri.path == 'web_gl' || - importUri.path == 'web_sql' || - importUri.path == 'html_common') || - // Mock web library path for testing. - importUri.path - .contains('native_null_assertions/js_invocations_in_web_library'); - if (node is ir.Library) return isWebLibrary(node.importUri); - return _isInWebLibrary(node.parent); - } - void _handleJsStringConcat(ir.StaticInvocation invocation) { if (_unexpectedForeignArguments(invocation, minPositional: 2, maxPositional: 2)) {
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index 3386187..aff9860 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -13,6 +13,7 @@ import '../elements/types.dart'; import '../inferrer/abstract_value_domain.dart'; import '../inferrer/types.dart'; +import '../ir/util.dart'; import '../js_backend/field_analysis.dart' show FieldAnalysisData, JFieldAnalysis; import '../js_backend/backend.dart' show CodegenInputs; @@ -925,11 +926,12 @@ HInstruction maybeAddNativeReturnNullCheck( HInstruction node, HInstruction replacement, FunctionEntity method) { - if (_options.enableNativeNullAssertions) { + if (_options.nativeNullAssertions) { if (method.library.isNonNullableByDefault) { FunctionType type = _closedWorld.elementEnvironment.getFunctionType(method); - if (_closedWorld.dartTypes.isNonNullableIfSound(type.returnType)) { + if (_closedWorld.dartTypes.isNonNullableIfSound(type.returnType) && + memberEntityIsInWebLibrary(method)) { node.block.addBefore(node, replacement); replacement = HNullCheck(replacement, _abstractValueDomain.excludeNull(replacement.instructionType),
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 31eb2d6..464ff44 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -4283,7 +4283,8 @@ _extensionTypes.isNativeClass(member.enclosingClass) && member is Procedure && member.function != null && - member.function.returnType.isPotentiallyNonNullable; + member.function.returnType.isPotentiallyNonNullable && + _isWebLibrary(member.enclosingLibrary?.importUri); // TODO(jmesserly): can we encapsulate REPL name lookups and remove this? // _emitMemberName would be a nice place to handle it, but we don't have @@ -5105,6 +5106,7 @@ } bool _isWebLibrary(Uri importUri) => + importUri != null && importUri.scheme == 'dart' && (importUri.path == 'html' || importUri.path == 'svg' ||
diff --git a/tests/dart2js/native/native_null_assertions/flag_disabled_test.dart b/tests/dart2js/native/native_null_assertions/flag_disabled_non_web_test.dart similarity index 81% copy from tests/dart2js/native/native_null_assertions/flag_disabled_test.dart copy to tests/dart2js/native/native_null_assertions/flag_disabled_non_web_test.dart index e307833..7ed8c58 100644 --- a/tests/dart2js/native/native_null_assertions/flag_disabled_test.dart +++ b/tests/dart2js/native/native_null_assertions/flag_disabled_non_web_test.dart
@@ -2,6 +2,9 @@ // 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. +// dart2jsOptions=--no-native-null-assertions + +import 'non_web_library_interfaces.dart'; import 'null_assertions_test_lib.dart'; void main() {
diff --git a/tests/dart2js/native/native_null_assertions/flag_disabled_test.dart b/tests/dart2js/native/native_null_assertions/flag_disabled_web_test.dart similarity index 81% rename from tests/dart2js/native/native_null_assertions/flag_disabled_test.dart rename to tests/dart2js/native/native_null_assertions/flag_disabled_web_test.dart index e307833..ee0d648 100644 --- a/tests/dart2js/native/native_null_assertions/flag_disabled_test.dart +++ b/tests/dart2js/native/native_null_assertions/flag_disabled_web_test.dart
@@ -2,7 +2,10 @@ // 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. +// dart2jsOptions=--no-native-null-assertions + import 'null_assertions_test_lib.dart'; +import 'web_library_interfaces.dart'; void main() { var flagEnabled = false;
diff --git a/tests/dart2js/native/native_null_assertions/flag_enabled_test.dart b/tests/dart2js/native/native_null_assertions/flag_enabled_non_web_test.dart similarity index 90% copy from tests/dart2js/native/native_null_assertions/flag_enabled_test.dart copy to tests/dart2js/native/native_null_assertions/flag_enabled_non_web_test.dart index 0eb3965..61c018b 100644 --- a/tests/dart2js/native/native_null_assertions/flag_enabled_test.dart +++ b/tests/dart2js/native/native_null_assertions/flag_enabled_non_web_test.dart
@@ -4,6 +4,7 @@ // dart2jsOptions=--native-null-assertions +import 'non_web_library_interfaces.dart'; import 'null_assertions_test_lib.dart'; void main() {
diff --git a/tests/dart2js/native/native_null_assertions/flag_enabled_test.dart b/tests/dart2js/native/native_null_assertions/flag_enabled_web_test.dart similarity index 91% rename from tests/dart2js/native/native_null_assertions/flag_enabled_test.dart rename to tests/dart2js/native/native_null_assertions/flag_enabled_web_test.dart index 0eb3965..71baddf 100644 --- a/tests/dart2js/native/native_null_assertions/flag_enabled_test.dart +++ b/tests/dart2js/native/native_null_assertions/flag_enabled_web_test.dart
@@ -5,6 +5,7 @@ // dart2jsOptions=--native-null-assertions import 'null_assertions_test_lib.dart'; +import 'web_library_interfaces.dart'; void main() { var flagEnabled = true;
diff --git a/tests/dart2js/native/native_null_assertions/js_invocations_in_non_web_library.dart b/tests/dart2js/native/native_null_assertions/js_invocations_in_non_web_library.dart deleted file mode 100644 index 9dd2be1..0000000 --- a/tests/dart2js/native/native_null_assertions/js_invocations_in_non_web_library.dart +++ /dev/null
@@ -1,16 +0,0 @@ -// 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. - -import '../native_testing.dart'; -import 'null_assertions_lib.dart'; - -// Implementation of `JSInterface` except in a folder that is not part of the -// allowlist for the `--native-null-assertions` flag. This file is not treated -// as a web library, and therefore the `JS()` invocations should not be checked. - -@Native('CCCInNonWebLibrary') -class CCCInNonWebLibrary implements JSInterface { - String get name => JS('String', '#.name', this); - String? get optName => JS('String|Null', '#.optName', this); -}
diff --git a/tests/dart2js/native/native_null_assertions/js_invocations_in_web_library.dart b/tests/dart2js/native/native_null_assertions/js_invocations_in_web_library.dart deleted file mode 100644 index 0554ab4..0000000 --- a/tests/dart2js/native/native_null_assertions/js_invocations_in_web_library.dart +++ /dev/null
@@ -1,16 +0,0 @@ -// 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. - -import '../native_testing.dart'; -import 'null_assertions_lib.dart'; - -// Implementation of `JSInterface` in a folder that is explicitly part of the -// allowlist for the `--native-null-assertions` flag. This file is treated as a -// web library, and therefore the `JS()` invocations should be checked. - -@Native('CCCInWebLibrary') -class CCCInWebLibrary implements JSInterface { - String get name => JS('String', '#.name', this); - String? get optName => JS('String|Null', '#.optName', this); -}
diff --git a/tests/dart2js/native/native_null_assertions/non_web_library_interfaces.dart b/tests/dart2js/native/native_null_assertions/non_web_library_interfaces.dart new file mode 100644 index 0000000..f0b6f95 --- /dev/null +++ b/tests/dart2js/native/native_null_assertions/non_web_library_interfaces.dart
@@ -0,0 +1,105 @@ +// 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. + +import '../native_testing.dart'; +import 'null_assertions_test_lib.dart'; + +// Implementations of `NativeInterface` and `JSInterface` in a folder that is +// not part of the allowlist for the `--native-null-assertions` flag. This file +// is not treated as a web library, and therefore native members and `JS()` +// invocations should not be checked. + +@Native("AAA") +class AAA implements NativeInterface { + int get size native; + String get name native; + String? get optName native; + int method1() native; + String method2() native; + String? optMethod() native; +} + +@Native('CCC') +class CCC implements JSInterface { + String get name => JS('String', '#.name', this); + String? get optName => JS('String|Null', '#.optName', this); +} + +/// Returns an 'AAA' object that satisfies the interface. +AAA makeA() native; + +/// Returns an 'AAA' object where each method breaks the interface's contract. +AAA makeAX() native; + +/// Returns a 'CCC' object that satisfies the interface using `JS()` +/// invocations. +CCC makeC() native; + +/// Returns a 'CCC' object where each method breaks the interface's contract. +CCC makeCX() native; + +// The 'AAA' version of the code is passed only objects of a single native +// class, so the native method can be inlined (which happens in the optimizer). +// This tests that the null-check exists in the 'inlined' code. + +@pragma('dart2js:noInline') +String describeAAA(AAA o) { + return '${o.name} ${o.method2()} ${o.size} ${o.method1()}'; +} + +@pragma('dart2js:noInline') +String describeOptAAA(AAA o) { + return '${o.optName} ${o.optMethod()}'; +} + +void testNativeNullAssertions(bool flagEnabled) { + nativeTesting(); + setup(); + AAA a = makeA(); + BBB b = BBB(); + + Expect.equals(expectedA, describeNativeInterface(a)); + Expect.equals(expectedB, describeNativeInterface(b)); + + Expect.equals(expectedA, describeAAA(a)); + + AAA x = makeAX(); // This object returns `null`! + // Since native members are not in a web library, there should be no checks, + // regardless of if the flag is enabled. + var checkExpectation = (f) => f(); + checkExpectation(() => describeNativeInterface(x)); + checkExpectation(() => describeAAA(x)); + + checkExpectation(() => x.name); + checkExpectation(() => x.size); + checkExpectation(() => x.method1()); + checkExpectation(() => x.method2()); + + // Now test that a nullable return type does not have a check. + Expect.equals(expectedOptA, describeOptNativeInterface(a)); + Expect.equals(expectedOptB, describeOptNativeInterface(b)); + Expect.equals(expectedOptX, describeOptNativeInterface(x)); + + Expect.equals(expectedOptA, describeOptAAA(a)); + Expect.equals(expectedOptX, describeOptAAA(x)); +} + +void testJSInvocationNullAssertions(bool flagEnabled) { + nativeTesting(); + setup(); + + CCC c = makeC(); + CCC cx = makeCX(); + + Expect.equals(expectedC, describeJSInterface(c)); + + // Since invocations are not in a web library, there should be no checks, + // regardless of if the flag is enabled. + var checkExpectation = (f) => f(); + checkExpectation(() => describeJSInterface(cx)); + + // Test that invocations with a nullable static type do not have checks. + Expect.equals(expectedOptC, describeOptJSInterface(c)); + Expect.equals(expectedOptCX, describeOptJSInterface(cx)); +}
diff --git a/tests/dart2js/native/native_null_assertions/null_assertions_lib.dart b/tests/dart2js/native/native_null_assertions/null_assertions_lib.dart deleted file mode 100644 index 894fdf0..0000000 --- a/tests/dart2js/native/native_null_assertions/null_assertions_lib.dart +++ /dev/null
@@ -1,38 +0,0 @@ -// 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. - -import '../native_testing.dart'; - -abstract class NativeInterface { - int get size; - String get name; - String? get optName; - int method1(); - String method2(); - String? optMethod(); -} - -@Native("AAA") -class AAA implements NativeInterface { - int get size native; - String get name native; - String? get optName native; - int method1() native; - String method2() native; - String? optMethod() native; -} - -abstract class JSInterface { - String get name; - String? get optName; -} - -class BBB implements NativeInterface { - int get size => 300; - String get name => 'Brenda'; - String? get optName => name; - int method1() => 400; - String method2() => 'brilliant!'; - String? optMethod() => method2(); -}
diff --git a/tests/dart2js/native/native_null_assertions/null_assertions_test_lib.dart b/tests/dart2js/native/native_null_assertions/null_assertions_test_lib.dart index 3f1f540..049cd36 100644 --- a/tests/dart2js/native/native_null_assertions/null_assertions_test_lib.dart +++ b/tests/dart2js/native/native_null_assertions/null_assertions_test_lib.dart
@@ -3,31 +3,29 @@ // BSD-style license that can be found in the LICENSE file. import '../native_testing.dart'; -import 'js_invocations_in_non_web_library.dart'; -import 'js_invocations_in_web_library.dart'; -import 'null_assertions_lib.dart'; -/// Returns an 'AAA' object that satisfies the interface. -AAA makeA() native; +abstract class NativeInterface { + int get size; + String get name; + String? get optName; + int method1(); + String method2(); + String? optMethod(); +} -/// Returns an 'AAA' object where each method breaks the interface's contract. -AAA makeAX() native; +abstract class JSInterface { + String get name; + String? get optName; +} -/// Returns a 'JSInterface' object whose `JS()` invocations exist in a library -/// that is part of the allowlist. -CCCInWebLibrary makeWebC() native; - -/// Returns the same as above but where each method breaks the interface's -/// contract. -CCCInWebLibrary makeWebCX() native; - -/// Returns a 'JSInterface' object whose `JS()` invocations exist in a library -/// that is not part of the allowlist. -CCCInNonWebLibrary makeNonWebC() native; - -/// Returns the same as above but where each method breaks the interface's -/// contract. -CCCInNonWebLibrary makeNonWebCX() native; +class BBB implements NativeInterface { + int get size => 300; + String get name => 'Brenda'; + String? get optName => name; + int method1() => 400; + String method2() => 'brilliant!'; + String? optMethod() => method2(); +} void setup() { JS('', r""" @@ -52,40 +50,25 @@ self.nativeConstructor(AAA); - function CCCInWebLibrary(n) { - this.name = n; - this.optName = n; - } - function CCCInNonWebLibrary(n) { + function CCC(n) { this.name = n; this.optName = n; } - makeWebC = function() { - return new CCCInWebLibrary('Carol'); + makeC = function() { + return new CCC('Carol'); }; - makeWebCX = function() { - return new CCCInWebLibrary(void 0); - }; - makeNonWebC = function() { - return new CCCInNonWebLibrary('Carol'); - }; - makeNonWebCX = function() { - return new CCCInNonWebLibrary(void 0); + makeCX = function() { + return new CCC(void 0); }; - self.nativeConstructor(CCCInWebLibrary); - self.nativeConstructor(CCCInNonWebLibrary); + self.nativeConstructor(CCC); })()"""); } // The 'NativeInterface' version of the code is passed both native and Dart // objects, so there will be an interceptor dispatch to the method. This tests // that the null-check exists in the forwarding method. -// -// The 'AAA' version of the code is passed only objects of a single native -// class, so the native method can be inlined (which happens in the optimizer). -// This tests that the null-check exists in the 'inlined' code. @pragma('dart2js:noInline') String describeNativeInterface(NativeInterface o) { @@ -93,21 +76,11 @@ } @pragma('dart2js:noInline') -String describeAAA(AAA o) { - return '${o.name} ${o.method2()} ${o.size} ${o.method1()}'; -} - -@pragma('dart2js:noInline') String describeOptNativeInterface(NativeInterface o) { return '${o.optName} ${o.optMethod()}'; } @pragma('dart2js:noInline') -String describeOptAAA(AAA o) { - return '${o.optName} ${o.optMethod()}'; -} - -@pragma('dart2js:noInline') String describeJSInterface(JSInterface o) { return '${o.name}'; } @@ -126,68 +99,3 @@ const expectedC = 'Carol'; const expectedOptC = 'Carol'; const expectedOptCX = 'null'; - -// Test that `--native-null-assertions` injects null-checks on the returned -// value of native methods with a non-nullable return type in an opt-in library. -void testNativeNullAssertions(bool flagEnabled) { - nativeTesting(); - setup(); - AAA a = makeA(); - BBB b = BBB(); - - Expect.equals(expectedA, describeNativeInterface(a)); - Expect.equals(expectedB, describeNativeInterface(b)); - - Expect.equals(expectedA, describeAAA(a)); - - AAA x = makeAX(); // This object returns `null`! - var checkExpectation = flagEnabled ? Expect.throws : (f) => f(); - checkExpectation(() => describeNativeInterface(x)); - checkExpectation(() => describeAAA(x)); - - checkExpectation(() => x.name); - checkExpectation(() => x.size); - checkExpectation(() => x.method1()); - checkExpectation(() => x.method2()); - - // Now test that a nullable return type does not have a check. - Expect.equals(expectedOptA, describeOptNativeInterface(a)); - Expect.equals(expectedOptB, describeOptNativeInterface(b)); - Expect.equals(expectedOptX, describeOptNativeInterface(x)); - - Expect.equals(expectedOptA, describeOptAAA(a)); - Expect.equals(expectedOptX, describeOptAAA(x)); -} - -// Test that `--native-null-assertions` injects null-checks on the returned -// value of `JS()` invocations with a non-nullable static type in an opt-in -// library. -void testJSInvocationNullAssertions(bool flagEnabled) { - nativeTesting(); - setup(); - - CCCInWebLibrary webC = makeWebC(); - CCCInWebLibrary webCX = makeWebCX(); - - CCCInNonWebLibrary nonWebC = makeNonWebC(); - CCCInNonWebLibrary nonWebCX = makeNonWebCX(); - - Expect.equals(expectedC, describeJSInterface(webC)); - Expect.equals(expectedC, describeJSInterface(nonWebC)); - - // If invocations are in a web library, this should throw if null checks are - // enabled. - var checkExpectationWeb = flagEnabled ? Expect.throws : (f) => f(); - checkExpectationWeb(() => describeJSInterface(webCX)); - - // If invocations are not in a web library, there should not be a null check - // regardless if the flag is enabled or not. - var checkExpectationNonWeb = (f) => f(); - checkExpectationNonWeb(() => describeJSInterface(nonWebCX)); - - // Test that invocations with a nullable static type do not have checks. - Expect.equals(expectedOptC, describeOptJSInterface(webC)); - Expect.equals(expectedOptC, describeOptJSInterface(nonWebC)); - Expect.equals(expectedOptCX, describeOptJSInterface(webCX)); - Expect.equals(expectedOptCX, describeOptJSInterface(nonWebCX)); -}
diff --git a/tests/dart2js/native/native_null_assertions/web_library_interfaces.dart b/tests/dart2js/native/native_null_assertions/web_library_interfaces.dart new file mode 100644 index 0000000..48bb86b --- /dev/null +++ b/tests/dart2js/native/native_null_assertions/web_library_interfaces.dart
@@ -0,0 +1,105 @@ +// 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. + +import '../native_testing.dart'; +import 'null_assertions_test_lib.dart'; + +// Implementations of `NativeInterface` and `JSInterface` in a folder that is +// part of the allowlist for the `--native-null-assertions` flag. This file is +// treated as a web library, and therefore native members and `JS()` invocations +// should be checked. + +@Native("AAA") +class AAA implements NativeInterface { + int get size native; + String get name native; + String? get optName native; + int method1() native; + String method2() native; + String? optMethod() native; +} + +@Native('CCC') +class CCC implements JSInterface { + String get name => JS('String', '#.name', this); + String? get optName => JS('String|Null', '#.optName', this); +} + +/// Returns an 'AAA' object that satisfies the interface. +AAA makeA() native; + +/// Returns an 'AAA' object where each method breaks the interface's contract. +AAA makeAX() native; + +/// Returns a 'CCC' object that satisfies the interface using `JS()` +/// invocations. +CCC makeC() native; + +/// Returns a 'CCC' object where each method breaks the interface's contract. +CCC makeCX() native; + +// The 'AAA' version of the code is passed only objects of a single native +// class, so the native method can be inlined (which happens in the optimizer). +// This tests that the null-check exists in the 'inlined' code. + +@pragma('dart2js:noInline') +String describeAAA(AAA o) { + return '${o.name} ${o.method2()} ${o.size} ${o.method1()}'; +} + +@pragma('dart2js:noInline') +String describeOptAAA(AAA o) { + return '${o.optName} ${o.optMethod()}'; +} + +void testNativeNullAssertions(bool flagEnabled) { + nativeTesting(); + setup(); + AAA a = makeA(); + BBB b = BBB(); + + Expect.equals(expectedA, describeNativeInterface(a)); + Expect.equals(expectedB, describeNativeInterface(b)); + + Expect.equals(expectedA, describeAAA(a)); + + AAA x = makeAX(); // This object returns `null`! + // Since native members are in a web library, this should throw if null checks + // are enabled. + var checkExpectation = flagEnabled ? Expect.throws : (f) => f(); + checkExpectation(() => describeNativeInterface(x)); + checkExpectation(() => describeAAA(x)); + + checkExpectation(() => x.name); + checkExpectation(() => x.size); + checkExpectation(() => x.method1()); + checkExpectation(() => x.method2()); + + // Now test that a nullable return type does not have a check. + Expect.equals(expectedOptA, describeOptNativeInterface(a)); + Expect.equals(expectedOptB, describeOptNativeInterface(b)); + Expect.equals(expectedOptX, describeOptNativeInterface(x)); + + Expect.equals(expectedOptA, describeOptAAA(a)); + Expect.equals(expectedOptX, describeOptAAA(x)); +} + +void testJSInvocationNullAssertions(bool flagEnabled) { + nativeTesting(); + setup(); + + CCC c = makeC(); + CCC cx = makeCX(); + + Expect.equals(expectedC, describeJSInterface(c)); + + // Since invocations are in a web library, this should throw if null checks + // are enabled. + var checkExpectation = flagEnabled ? Expect.throws : (f) => f(); + checkExpectation(() => describeJSInterface(cx)); + + // Test that invocations with a nullable static type do not have checks. + Expect.equals(expectedOptC, describeOptJSInterface(c)); + Expect.equals(expectedOptCX, describeOptJSInterface(cx)); +}
diff --git a/tools/VERSION b/tools/VERSION index dcbe123..0e82789a 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -27,5 +27,5 @@ MAJOR 2 MINOR 11 PATCH 0 -PRERELEASE 247 +PRERELEASE 248 PRERELEASE_PATCH 0 \ No newline at end of file