[ddc, dartj2s] Exclude null checks on non-web native members

Modifies ddc and dart2js logic to only include null-checks on native
members inside the web libraries. Modifies tests to account for this
change.

Change-Id: If9c164fb90b761d3c4611d87ffeb02c2fa884457
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168585
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
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/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 619683d..aa1ea95 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1548,7 +1548,8 @@
       if (options.enableNativeNullAssertions) {
         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();
@@ -4800,7 +4801,7 @@
   /// type is non-nullable, add a check to make sure it isn't null.
   void _maybeAddNullCheckOnJS(ir.StaticInvocation invocation) {
     if (options.enableNativeNullAssertions &&
-        _isInWebLibrary(invocation) &&
+        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..6011731 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;
@@ -929,7 +930,8 @@
       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 90%
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..85521eb 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,7 @@
 // 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 '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 90%
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..7bfe293 100644
--- a/tests/dart2js/native/native_null_assertions/flag_disabled_test.dart
+++ b/tests/dart2js/native/native_null_assertions/flag_disabled_web_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 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));
+}