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