Version 2.12.0-179.0.dev

Merge commit 'c7b6e6aa99e76e44d7f807c90ac9f1fc229a9968' into 'dev'
diff --git a/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart b/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
index 5cade7b..7f4c3c9 100644
--- a/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
+++ b/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart
@@ -4,8 +4,6 @@
 
 // ignore_for_file: avoid_function_literals_in_foreach_calls
 
-import 'dart:math' show pow;
-
 import 'package:benchmark_harness/benchmark_harness.dart';
 import 'package:fixnum/fixnum.dart';
 
@@ -33,29 +31,43 @@
 // integers.
 const requiredDigits = 11106;
 
-class ParseBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final BigInt seed;
-  final List<String> strings = [];
-
-  ParseBigIntBenchmark(String name, this.bits)
-      : seed = (BigInt.one << bits) - BigInt.one,
+class Benchmark extends BenchmarkBase {
+  final List<String> strings;
+  Benchmark(String name, int bits, {bool forInt: false})
+      : strings = generateStrings(bits, forInt),
         super(name);
 
-  @override
-  void setup() {
+  static List<String> generateStrings(int bits, bool forInt) {
+    List<String> strings = [];
+    BigInt seed = (BigInt.one << bits) - BigInt.one;
     var b = seed;
+    var restartDelta = BigInt.zero;
     var totalLength = 0;
     while (totalLength < requiredDigits) {
       if (b.bitLength < bits) {
-        b = seed;
+        restartDelta += seed >> 20;
+        restartDelta += BigInt.one;
+        // Restart from a slighly reduced seed to generate different numbers.
+        b = seed - restartDelta;
       }
-      final string = b.toString();
+      var string = b.toString();
+
+      // Web integers lose precision due to rounding for larger values. Make
+      // sure the string will round-trip correctly.
+      if (forInt) string = int.parse(string).toString();
+
       strings.add(string);
       totalLength += string.length;
-      b = b - (b >> 8);
+      var delta = b >> 8;
+      if (delta == BigInt.zero) delta = BigInt.one;
+      b = b - delta;
     }
+    return strings;
   }
+}
+
+class ParseBigIntBenchmark extends Benchmark {
+  ParseBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -68,31 +80,8 @@
   }
 }
 
-int int64UnsignedBitLength(Int64 i) => i.isNegative ? 64 : i.bitLength;
-
-class ParseInt64Benchmark extends BenchmarkBase {
-  final int bits;
-  final Int64 seed;
-  final List<String> strings = [];
-
-  ParseInt64Benchmark(String name, this.bits)
-      : seed = (Int64.ONE << bits) - Int64.ONE,
-        super(name);
-
-  @override
-  void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (int64UnsignedBitLength(b) < bits) {
-        b = seed;
-      }
-      final string = b.toStringUnsigned();
-      strings.add(string);
-      totalLength += string.length;
-      b = b - b.shiftRightUnsigned(8);
-    }
-  }
+class ParseInt64Benchmark extends Benchmark {
+  ParseInt64Benchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -105,29 +94,8 @@
   }
 }
 
-class ParseIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final int seed;
-  final List<String> strings = [];
-
-  ParseIntBenchmark(String name, this.bits)
-      : seed = (pow(2, bits) as int) - 1,
-        super(name);
-
-  @override
-  void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (b.bitLength < bits) {
-        b = seed;
-      }
-      final string = b.toString();
-      strings.add(string);
-      totalLength += string.length;
-      b = b - b ~/ 256;
-    }
-  }
+class ParseIntBenchmark extends Benchmark {
+  ParseIntBenchmark(String name, int bits) : super(name, bits, forInt: true);
 
   @override
   void run() {
@@ -140,33 +108,8 @@
   }
 }
 
-class ParseJsBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final Object seed;
-  final List<String> strings = [];
-
-  ParseJsBigIntBenchmark(String name, this.bits)
-      : seed = nativeBigInt.subtract(
-            nativeBigInt.shiftLeft(
-                nativeBigInt.one, nativeBigInt.fromInt(bits)),
-            nativeBigInt.one),
-        super(name);
-
-  @override
-  void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (nativeBigInt.bitLength(b) < bits) {
-        b = seed;
-      }
-      final string = nativeBigInt.toStringMethod(b);
-      strings.add(string);
-      totalLength += string.length;
-      b = nativeBigInt.subtract(
-          b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
-    }
-  }
+class ParseJsBigIntBenchmark extends Benchmark {
+  ParseJsBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -179,27 +122,16 @@
   }
 }
 
-class FormatBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final BigInt seed;
+class FormatBigIntBenchmark extends Benchmark {
   final List<BigInt> values = [];
 
-  FormatBigIntBenchmark(String name, this.bits)
-      : seed = (BigInt.one << bits) - BigInt.one,
-        super(name);
+  FormatBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (b.bitLength < bits) {
-        b = seed;
-      }
-      final string = b.toString();
+    for (String s in strings) {
+      BigInt b = BigInt.parse(s);
       values.add(b - BigInt.one); // We add 'one' back later.
-      totalLength += string.length;
-      b = b - (b >> 8);
     }
   }
 
@@ -218,28 +150,16 @@
   }
 }
 
-class FormatIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final int seed;
+class FormatIntBenchmark extends Benchmark {
   final List<int> values = [];
 
-  FormatIntBenchmark(String name, this.bits)
-      : seed = (pow(2, bits) as int) - 1,
-        super(name);
+  FormatIntBenchmark(String name, int bits) : super(name, bits, forInt: true);
 
   @override
   void setup() {
-    var b = seed;
-    var totalLength = 0;
-    int kk = b ~/ 100000;
-    while (totalLength < requiredDigits) {
-      if (b.bitLength < bits) {
-        b = seed - ++kk;
-      }
-      final string = b.toString();
-      values.add(b - 4096); // We add 'one' back later.
-      totalLength += string.length;
-      b = b - (b ~/ 256);
+    for (String s in strings) {
+      int b = int.parse(s);
+      values.add(b - 4096); // We add this back later.
     }
   }
 
@@ -247,7 +167,9 @@
   void run() {
     for (final b0 in values) {
       // Instances might cache `toString()`, so use arithmetic to create a new
-      // instance to try to protect against measuring a cached string.
+      // instance to try to protect against measuring a cached string.  We use
+      // 4096 to avoid the arithmetic being a no-op due to rounding on web
+      // integers (i.e. doubles).
       final b = b0 + 4096;
       final s = b.toString();
       sink1 = s;
@@ -257,27 +179,16 @@
   }
 }
 
-class FormatInt64Benchmark extends BenchmarkBase {
-  final int bits;
-  final Int64 seed;
+class FormatInt64Benchmark extends Benchmark {
   final List<Int64> values = [];
 
-  FormatInt64Benchmark(String name, this.bits)
-      : seed = (Int64.ONE << bits) - Int64.ONE,
-        super(name);
+  FormatInt64Benchmark(String name, int bits) : super(name, bits);
 
   @override
   void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (int64UnsignedBitLength(b) < bits) {
-        b = seed;
-      }
-      final string = b.toStringUnsigned();
-      values.add(b - Int64.ONE);
-      totalLength += string.length;
-      b = b - b.shiftRightUnsigned(8);
+    for (String s in strings) {
+      final b = Int64.parseInt(s);
+      values.add(b - Int64.ONE); // We add this back later.
     }
   }
 
@@ -296,32 +207,17 @@
   }
 }
 
-class FormatJsBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final Object seed;
+class FormatJsBigIntBenchmark extends Benchmark {
   final List<Object> values = [];
 
-  FormatJsBigIntBenchmark(String name, this.bits)
-      : seed = nativeBigInt.subtract(
-            nativeBigInt.shiftLeft(
-                nativeBigInt.one, nativeBigInt.fromInt(bits)),
-            nativeBigInt.one),
-        super(name);
+  FormatJsBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void setup() {
     final one = nativeBigInt.one;
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (nativeBigInt.bitLength(b) < bits) {
-        b = seed;
-      }
-      final string = nativeBigInt.toStringMethod(b);
-      values.add(nativeBigInt.subtract(b, one)); // We add 'one' back later.
-      totalLength += string.length;
-      b = nativeBigInt.subtract(
-          b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
+    for (String s in strings) {
+      final b = nativeBigInt.parse(s);
+      values.add(nativeBigInt.subtract(b, one)); // We add this back later.
     }
   }
 
@@ -371,6 +267,9 @@
   final benchmarks = [
     () => ParseIntBenchmark('Int.parse.0009.bits', 9),
     () => ParseIntBenchmark('Int.parse.0032.bits', 32),
+    // Use '63' bits to avoid 64-bit arithmetic overflowing to negative. Keep
+    // the name as '64' to help comparisons.  The effect of an incorrect number
+    // is reduced since benchmark results are normalized to a 'per digit' score
     () => ParseIntBenchmark('Int.parse.0064.bits', 63),
     () => ParseInt64Benchmark('Int64.parse.0009.bits', 9),
     () => ParseInt64Benchmark('Int64.parse.0032.bits', 32),
@@ -389,7 +288,7 @@
     selectParseNativeBigIntBenchmark('JsBigInt.parse.4096.bits', 4096),
     () => FormatIntBenchmark('Int.toString.0009.bits', 9),
     () => FormatIntBenchmark('Int.toString.0032.bits', 32),
-    () => FormatIntBenchmark('Int.toString.0064.bits', 63),
+    () => FormatIntBenchmark('Int.toString.0064.bits', 63), // '63': See above.
     () => FormatInt64Benchmark('Int64.toString.0009.bits', 9),
     () => FormatInt64Benchmark('Int64.toString.0032.bits', 32),
     () => FormatInt64Benchmark('Int64.toString.0064.bits', 64),
diff --git a/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart b/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
index 26a796f..700c408 100644
--- a/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
+++ b/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
@@ -33,29 +33,43 @@
 // integers.
 const requiredDigits = 11106;
 
-class ParseBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final BigInt seed;
-  final List<String> strings = [];
-
-  ParseBigIntBenchmark(String name, this.bits)
-      : seed = (BigInt.one << bits) - BigInt.one,
+class Benchmark extends BenchmarkBase {
+  final List<String> strings;
+  Benchmark(String name, int bits, {bool forInt: false})
+      : strings = generateStrings(bits, forInt),
         super(name);
 
-  @override
-  void setup() {
+  static List<String> generateStrings(int bits, bool forInt) {
+    List<String> strings = [];
+    BigInt seed = (BigInt.one << bits) - BigInt.one;
     var b = seed;
+    var restartDelta = BigInt.zero;
     var totalLength = 0;
     while (totalLength < requiredDigits) {
       if (b.bitLength < bits) {
-        b = seed;
+        restartDelta += seed >> 20;
+        restartDelta += BigInt.one;
+        // Restart from a slighly reduced seed to generate different numbers.
+        b = seed - restartDelta;
       }
-      final string = b.toString();
+      var string = b.toString();
+
+      // Web integers lose precision due to rounding for larger values. Make
+      // sure the string will round-trip correctly.
+      if (forInt) string = int.parse(string).toString();
+
       strings.add(string);
       totalLength += string.length;
-      b = b - (b >> 8);
+      var delta = b >> 8;
+      if (delta == BigInt.zero) delta = BigInt.one;
+      b = b - delta;
     }
+    return strings;
   }
+}
+
+class ParseBigIntBenchmark extends Benchmark {
+  ParseBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -68,31 +82,8 @@
   }
 }
 
-int int64UnsignedBitLength(Int64 i) => i.isNegative ? 64 : i.bitLength;
-
-class ParseInt64Benchmark extends BenchmarkBase {
-  final int bits;
-  final Int64 seed;
-  final List<String> strings = [];
-
-  ParseInt64Benchmark(String name, this.bits)
-      : seed = (Int64.ONE << bits) - Int64.ONE,
-        super(name);
-
-  @override
-  void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (int64UnsignedBitLength(b) < bits) {
-        b = seed;
-      }
-      final string = b.toStringUnsigned();
-      strings.add(string);
-      totalLength += string.length;
-      b = b - b.shiftRightUnsigned(8);
-    }
-  }
+class ParseInt64Benchmark extends Benchmark {
+  ParseInt64Benchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -105,33 +96,22 @@
   }
 }
 
-class ParseJsBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final Object seed;
-  final List<String> strings = [];
-
-  ParseJsBigIntBenchmark(String name, this.bits)
-      : seed = nativeBigInt.subtract(
-            nativeBigInt.shiftLeft(
-                nativeBigInt.one, nativeBigInt.fromInt(bits)),
-            nativeBigInt.one),
-        super(name);
+class ParseIntBenchmark extends Benchmark {
+  ParseIntBenchmark(String name, int bits) : super(name, bits, forInt: true);
 
   @override
-  void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (nativeBigInt.bitLength(b) < bits) {
-        b = seed;
-      }
-      final string = nativeBigInt.toStringMethod(b);
-      strings.add(string);
-      totalLength += string.length;
-      b = nativeBigInt.subtract(
-          b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
+  void run() {
+    for (final s in strings) {
+      final b = int.parse(s);
+      sink1 = s;
+      sink2 = b;
     }
+    check(sink2.isEven);
   }
+}
+
+class ParseJsBigIntBenchmark extends Benchmark {
+  ParseJsBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void run() {
@@ -144,27 +124,16 @@
   }
 }
 
-class FormatBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final BigInt seed;
+class FormatBigIntBenchmark extends Benchmark {
   final List<BigInt> values = [];
 
-  FormatBigIntBenchmark(String name, this.bits)
-      : seed = (BigInt.one << bits) - BigInt.one,
-        super(name);
+  FormatBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (b.bitLength < bits) {
-        b = seed;
-      }
-      final string = b.toString();
+    for (String s in strings) {
+      BigInt b = BigInt.parse(s);
       values.add(b - BigInt.one); // We add 'one' back later.
-      totalLength += string.length;
-      b = b - (b >> 8);
     }
   }
 
@@ -183,27 +152,45 @@
   }
 }
 
-class FormatInt64Benchmark extends BenchmarkBase {
-  final int bits;
-  final Int64 seed;
-  final List<Int64> values = [];
+class FormatIntBenchmark extends Benchmark {
+  final List<int> values = [];
 
-  FormatInt64Benchmark(String name, this.bits)
-      : seed = (Int64.ONE << bits) - Int64.ONE,
-        super(name);
+  FormatIntBenchmark(String name, int bits) : super(name, bits, forInt: true);
 
   @override
   void setup() {
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (int64UnsignedBitLength(b) < bits) {
-        b = seed;
-      }
-      final string = b.toStringUnsigned();
-      values.add(b - Int64.ONE);
-      totalLength += string.length;
-      b = b - b.shiftRightUnsigned(8);
+    for (String s in strings) {
+      int b = int.parse(s);
+      values.add(b - 4096); // We add this back later.
+    }
+  }
+
+  @override
+  void run() {
+    for (final b0 in values) {
+      // Instances might cache `toString()`, so use arithmetic to create a new
+      // instance to try to protect against measuring a cached string.  We use
+      // 4096 to avoid the arithmetic being a no-op due to rounding on web
+      // integers (i.e. doubles).
+      final b = b0 + 4096;
+      final s = b.toString();
+      sink1 = s;
+      sink2 = b;
+    }
+    check(sink2.isEven);
+  }
+}
+
+class FormatInt64Benchmark extends Benchmark {
+  final List<Int64> values = [];
+
+  FormatInt64Benchmark(String name, int bits) : super(name, bits);
+
+  @override
+  void setup() {
+    for (String s in strings) {
+      final b = Int64.parseInt(s);
+      values.add(b - Int64.ONE); // We add this back later.
     }
   }
 
@@ -222,32 +209,17 @@
   }
 }
 
-class FormatJsBigIntBenchmark extends BenchmarkBase {
-  final int bits;
-  final Object seed;
+class FormatJsBigIntBenchmark extends Benchmark {
   final List<Object> values = [];
 
-  FormatJsBigIntBenchmark(String name, this.bits)
-      : seed = nativeBigInt.subtract(
-            nativeBigInt.shiftLeft(
-                nativeBigInt.one, nativeBigInt.fromInt(bits)),
-            nativeBigInt.one),
-        super(name);
+  FormatJsBigIntBenchmark(String name, int bits) : super(name, bits);
 
   @override
   void setup() {
     final one = nativeBigInt.one;
-    var b = seed;
-    var totalLength = 0;
-    while (totalLength < requiredDigits) {
-      if (nativeBigInt.bitLength(b) < bits) {
-        b = seed;
-      }
-      final string = nativeBigInt.toStringMethod(b);
-      values.add(nativeBigInt.subtract(b, one)); // We add 'one' back later.
-      totalLength += string.length;
-      b = nativeBigInt.subtract(
-          b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
+    for (String s in strings) {
+      final b = nativeBigInt.parse(s);
+      values.add(nativeBigInt.subtract(b, one)); // We add this back later.
     }
   }
 
@@ -295,6 +267,12 @@
 
 void main() {
   final benchmarks = [
+    () => ParseIntBenchmark('Int.parse.0009.bits', 9),
+    () => ParseIntBenchmark('Int.parse.0032.bits', 32),
+    // Use '63' bits to avoid 64-bit arithmetic overflowing to negative. Keep
+    // the name as '64' to help comparisons.  The effect of an incorrect number
+    // is reduced since benchmark results are normalized to a 'per digit' score
+    () => ParseIntBenchmark('Int.parse.0064.bits', 63),
     () => ParseInt64Benchmark('Int64.parse.0009.bits', 9),
     () => ParseInt64Benchmark('Int64.parse.0032.bits', 32),
     () => ParseInt64Benchmark('Int64.parse.0064.bits', 64),
@@ -310,6 +288,9 @@
     selectParseNativeBigIntBenchmark('JsBigInt.parse.0256.bits', 256),
     selectParseNativeBigIntBenchmark('JsBigInt.parse.1024.bits', 1024),
     selectParseNativeBigIntBenchmark('JsBigInt.parse.4096.bits', 4096),
+    () => FormatIntBenchmark('Int.toString.0009.bits', 9),
+    () => FormatIntBenchmark('Int.toString.0032.bits', 32),
+    () => FormatIntBenchmark('Int.toString.0064.bits', 63), // '63': See above.
     () => FormatInt64Benchmark('Int64.toString.0009.bits', 9),
     () => FormatInt64Benchmark('Int64.toString.0032.bits', 32),
     () => FormatInt64Benchmark('Int64.toString.0064.bits', 64),
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 0faba7a..649b7df 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -5934,6 +5934,39 @@
     tip: r"""Try replacing them with normal or optional parameters.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String name,
+        String name2,
+        String
+            string3)> templateJsInteropNativeClassInAnnotation = const Template<
+        Message Function(String name, String name2, String string3)>(
+    messageTemplate:
+        r"""JS interop class '#name' conflicts with natively supported class '#name2' in '#string3'.""",
+    withArguments: _withArgumentsJsInteropNativeClassInAnnotation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, String name2, String string3)>
+    codeJsInteropNativeClassInAnnotation =
+    const Code<Message Function(String name, String name2, String string3)>(
+  "JsInteropNativeClassInAnnotation",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsJsInteropNativeClassInAnnotation(
+    String name, String name2, String string3) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  if (string3.isEmpty) throw 'No string provided';
+  return new Message(codeJsInteropNativeClassInAnnotation,
+      message:
+          """JS interop class '${name}' conflicts with natively supported class '${name2}' in '${string3}'.""",
+      arguments: {'name': name, 'name2': name2, 'string3': string3});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeJsInteropNonExternalConstructor =
     messageJsInteropNonExternalConstructor;
 
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 8bb8c6d..91405e5 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -17,17 +17,22 @@
         messageJsInteropNonExternalConstructor,
         messageJsInteropNonExternalMember,
         templateJsInteropDartClassExtendsJSClass,
-        templateJsInteropJSClassExtendsDartClass;
+        templateJsInteropJSClassExtendsDartClass,
+        templateJsInteropNativeClassInAnnotation;
 
 import 'src/js_interop.dart';
 
 class JsInteropChecks extends RecursiveVisitor<void> {
   final CoreTypes _coreTypes;
   final DiagnosticReporter<Message, LocatedMessage> _diagnosticsReporter;
+  final Map<String, Class> _nativeClasses;
   bool _classHasJSAnnotation = false;
+  bool _classHasAnonymousAnnotation = false;
   bool _libraryHasJSAnnotation = false;
+  bool _libraryIsGlobalNamespace = false;
 
-  JsInteropChecks(this._coreTypes, this._diagnosticsReporter);
+  JsInteropChecks(
+      this._coreTypes, this._diagnosticsReporter, this._nativeClasses);
 
   @override
   void defaultMember(Member member) {
@@ -40,6 +45,7 @@
   @override
   void visitClass(Class cls) {
     _classHasJSAnnotation = hasJSInteropAnnotation(cls);
+    _classHasAnonymousAnnotation = hasAnonymousAnnotation(cls);
     var superclass = cls.superclass;
     if (superclass != null && superclass != _coreTypes.objectClass) {
       var superHasJSAnnotation = hasJSInteropAnnotation(superclass);
@@ -59,14 +65,53 @@
             cls.location.file);
       }
     }
+    if (_classHasJSAnnotation &&
+        !_classHasAnonymousAnnotation &&
+        _libraryIsGlobalNamespace) {
+      var jsClass = getJSName(cls);
+      if (jsClass.isEmpty) {
+        // No rename, take the name of the class directly.
+        jsClass = cls.name;
+      } else {
+        // Remove any global prefixes. Regex here is greedy and will only return
+        // a value for `className` that doesn't start with 'self.' or 'window.'.
+        var classRegexp = new RegExp(r'^((self|window)\.)*(?<className>.*)$');
+        var matches = classRegexp.allMatches(jsClass);
+        jsClass = matches.first.namedGroup('className');
+      }
+      if (_nativeClasses.containsKey(jsClass)) {
+        var nativeClass = _nativeClasses[jsClass];
+        _diagnosticsReporter.report(
+            templateJsInteropNativeClassInAnnotation.withArguments(
+                cls.name,
+                nativeClass.name,
+                nativeClass.enclosingLibrary.importUri.toString()),
+            cls.fileOffset,
+            cls.name.length,
+            cls.location.file);
+      }
+    }
     super.visitClass(cls);
+    _classHasAnonymousAnnotation = false;
     _classHasJSAnnotation = false;
   }
 
   @override
   void visitLibrary(Library lib) {
     _libraryHasJSAnnotation = hasJSInteropAnnotation(lib);
+    _libraryIsGlobalNamespace = false;
+    if (_libraryHasJSAnnotation) {
+      var libraryAnnotation = getJSName(lib);
+      var globalRegexp = new RegExp(r'^(self|window)(\.(self|window))*$');
+      if (libraryAnnotation.isEmpty ||
+          globalRegexp.hasMatch(libraryAnnotation)) {
+        _libraryIsGlobalNamespace = true;
+      }
+    } else {
+      _libraryIsGlobalNamespace = true;
+    }
     super.visitLibrary(lib);
+    _libraryIsGlobalNamespace = false;
     _libraryHasJSAnnotation = false;
   }
 
@@ -98,7 +143,7 @@
     }
 
     var isAnonymousFactory =
-        isAnonymousClassMember(procedure) && procedure.isFactory;
+        _classHasAnonymousAnnotation && procedure.isFactory;
 
     if (isAnonymousFactory) {
       if (procedure.function != null &&
diff --git a/pkg/_js_interop_checks/lib/src/js_interop.dart b/pkg/_js_interop_checks/lib/src/js_interop.dart
index 33ab00b..7831f2d 100644
--- a/pkg/_js_interop_checks/lib/src/js_interop.dart
+++ b/pkg/_js_interop_checks/lib/src/js_interop.dart
@@ -9,15 +9,48 @@
 bool hasJSInteropAnnotation(Annotatable a) =>
     a.annotations.any(_isPublicJSAnnotation);
 
-/// Returns true if [m] belongs to an anonymous class.
-bool isAnonymousClassMember(Member m) {
-  var enclosingClass = m.enclosingClass;
-  if (enclosingClass == null) return false;
-  return enclosingClass.annotations.any(_isAnonymousAnnotation);
+/// Returns true iff the node has an `@anonymous(...)` annotation from
+/// `package:js` or from the internal `dart:_js_annotations`.
+bool hasAnonymousAnnotation(Annotatable a) =>
+    a.annotations.any(_isAnonymousAnnotation);
+
+/// If [a] has a `@JS('...')` annotation, returns the value inside the
+/// parentheses.
+///
+/// If there is none or the class does not have a `@JS()` annotation, returns
+/// an empty String.
+String getJSName(Annotatable a) {
+  String jsClass = '';
+  for (var annotation in a.annotations) {
+    if (_isPublicJSAnnotation(annotation)) {
+      var jsClasses = _stringAnnotationValues(annotation);
+      if (jsClasses.length > 0) {
+        jsClass = jsClasses[0];
+      }
+    }
+  }
+  return jsClass;
+}
+
+/// If [a] has a `@Native('...')` annotation, returns the values inside the
+/// parentheses.
+///
+/// If there are none or the class does not have a `@Native()` annotation,
+/// returns an empty list. Unlike `@JS()`, the string within `@Native()` is
+/// allowed to contain several classes separated by a `,`.
+List<String> getNativeNames(Annotatable a) {
+  List<String> nativeClasses = [];
+  for (var annotation in a.annotations) {
+    if (_isNativeAnnotation(annotation)) {
+      nativeClasses.addAll(_stringAnnotationValues(annotation));
+    }
+  }
+  return nativeClasses;
 }
 
 final _packageJs = Uri.parse('package:js/js.dart');
 final _internalJs = Uri.parse('dart:_js_annotations');
+final _jsHelper = Uri.parse('dart:_js_helper');
 
 /// Returns true if [value] is the `JS` annotation from `package:js` or from
 /// `dart:_js_annotations`.
@@ -39,12 +72,20 @@
           c.enclosingLibrary.importUri == _internalJs);
 }
 
+bool _isNativeAnnotation(Expression value) {
+  var c = _annotationClass(value);
+  return c != null &&
+      c.name == 'Native' &&
+      c.enclosingLibrary.importUri == _jsHelper;
+}
+
 /// Returns the class of the instance referred to by metadata annotation [node].
 ///
 /// For example:
 ///
 /// - `@JS()` would return the "JS" class in "package:js".
 /// - `@anonymous` would return the "_Anonymous" class in "package:js".
+/// - `@Native` would return the "Native" class in "dart:_js_helper".
 ///
 /// This function works regardless of whether the CFE is evaluating constants,
 /// or whether the constant is a field reference (such as "anonymous" above).
@@ -60,3 +101,40 @@
   }
   return null;
 }
+
+/// Returns the string values inside of a metadata annotation [node].
+///
+/// For example:
+/// - `@JS('Foo')` would return ['Foo'].
+/// - `@Native('Foo,Bar')` would return ['Foo', 'Bar'].
+///
+/// [node] is expected to be an annotation with either StringConstants or
+/// StringLiterals that can be made up of multiple values. If there are none,
+/// this method returns an empty list. This method throws an assertion if there
+/// are multiple arguments or a named arg in the annotation.
+List<String> _stringAnnotationValues(Expression node) {
+  List<String> values = [];
+  if (node is ConstantExpression) {
+    var constant = node.constant;
+    if (constant is InstanceConstant) {
+      var argLength = constant.fieldValues.values.length;
+      if (argLength == 1) {
+        var value = constant.fieldValues.values.elementAt(0);
+        if (value is StringConstant) values.addAll(value.value.split(','));
+      } else if (argLength > 1) {
+        throw new ArgumentError('Method expects annotation with at most one '
+            'positional argument: $node.');
+      }
+    }
+  } else if (node is ConstructorInvocation) {
+    var argLength = node.arguments.positional.length;
+    if (argLength > 1 || node.arguments.named.length > 0) {
+      throw new ArgumentError('Method expects annotation with at most one '
+          'positional argument: $node.');
+    } else if (argLength == 1) {
+      var value = node.arguments.positional[0];
+      if (value is StringLiteral) values.addAll(value.value.split(','));
+    }
+  }
+  return values;
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 2f7c026..4a475fe 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -628,7 +628,7 @@
     var uriObj = Uri.parse(uri);
     var file = _fsState.getFileForUri(uriObj);
 
-    if (file.isUnresolved) {
+    if (file == null) {
       throw ArgumentError('$uri cannot be resolved to a file.');
     }
 
@@ -885,7 +885,8 @@
   /// without a package name. In these cases we cannot prove that the file is
   /// not a part, so it must be a library.
   bool isLibraryByUri(Uri uri) {
-    return !_fsState.getFileForUri(uri).isPart;
+    var file = _fsState.getFileForUri(uri);
+    return file == null || !file.isPart;
   }
 
   /// Return a [Future] that completes with a [ParsedUnitResult] for the file
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 942e449..0e20865 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -17,7 +17,6 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/defined_names.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
 import 'package:analyzer/src/dart/analysis/library_graph.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -37,6 +36,7 @@
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/summary2/bundle_writer.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
+import 'package:collection/collection.dart';
 import 'package:convert/convert.dart';
 import 'package:crypto/crypto.dart';
 import 'package:meta/meta.dart';
@@ -197,9 +197,9 @@
   /// parted.
   Set<FileState> get directReferencedFiles {
     return _directReferencedFiles ??= <FileState>{
-      ...importedFiles,
-      ...exportedFiles,
-      ...partedFiles,
+      ...importedFiles.whereNotNull(),
+      ...exportedFiles.whereNotNull(),
+      ...partedFiles.whereNotNull(),
     };
   }
 
@@ -207,8 +207,8 @@
   /// exported.
   Set<FileState> get directReferencedLibraries {
     return _directReferencedLibraries ??= <FileState>{
-      ...importedFiles,
-      ...exportedFiles,
+      ...importedFiles.whereNotNull(),
+      ...exportedFiles.whereNotNull(),
     };
   }
 
@@ -263,10 +263,6 @@
     return !_unlinked2.hasLibraryDirective && _unlinked2.hasPartOfDirective;
   }
 
-  /// Return `true` if the file is the "unresolved" file, which does not have
-  /// neither a valid URI, nor a path.
-  bool get isUnresolved => uri == null;
-
   /// If the file [isPart], return a currently know library the file is a part
   /// of. Return `null` if a library is not known, for example because we have
   /// not processed a library file yet.
@@ -300,7 +296,10 @@
   /// The list of files files that this library consists of, i.e. this library
   /// file itself and its [partedFiles].
   List<FileState> get libraryFiles {
-    return _libraryFiles ??= [this, ...partedFiles];
+    return _libraryFiles ??= [
+      this,
+      ...partedFiles.whereNotNull(),
+    ];
   }
 
   /// Return information about line in the file.
@@ -313,9 +312,11 @@
       for (var uri in _unlinked2.parts) {
         var file = _fileForRelativeUri(uri);
         _partedFiles.add(file);
-        _fsState._partToLibraries
-            .putIfAbsent(file, () => <FileState>[])
-            .add(this);
+        if (file != null) {
+          _fsState._partToLibraries
+              .putIfAbsent(file, () => <FileState>[])
+              .add(this);
+        }
       }
     }
     return _partedFiles;
@@ -558,18 +559,18 @@
     return unit;
   }
 
-  /// Return the [FileState] for the given [relativeUri], maybe "unresolved"
-  /// file if the URI cannot be parsed, cannot correspond any file, etc.
+  /// Return the [FileState] for the given [relativeUri], or `null` if the
+  /// URI cannot be parsed, cannot correspond any file, etc.
   FileState _fileForRelativeUri(String relativeUri) {
     if (relativeUri.isEmpty) {
-      return _fsState.unresolvedFile;
+      return null;
     }
 
     Uri absoluteUri;
     try {
       absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
     } on FormatException {
-      return _fsState.unresolvedFile;
+      return null;
     }
 
     return _fsState.getFileForUri(absoluteUri);
@@ -799,9 +800,6 @@
   /// The value of this field is incremented when the set of files is updated.
   int fileStamp = 0;
 
-  /// The [FileState] instance that correspond to an unresolved URI.
-  FileState _unresolvedFile;
-
   /// The cache of content of files, possibly shared with other file system
   /// states with the same resource provider and the content overlay.
   _FileContentCache _fileContentCache;
@@ -834,18 +832,6 @@
   @visibleForTesting
   FileSystemStateTestView get test => _testView;
 
-  /// Return the [FileState] instance that correspond to an unresolved URI.
-  /// TODO(scheglov) Remove it.
-  FileState get unresolvedFile {
-    if (_unresolvedFile == null) {
-      var featureSet = FeatureSet.latestLanguageVersion();
-      _unresolvedFile = FileState._(this, null, null, null, null, featureSet,
-          ExperimentStatus.currentVersion);
-      _unresolvedFile.refresh();
-    }
-    return _unresolvedFile;
-  }
-
   FeatureSet contextFeatureSet(
     String path,
     Uri uri,
@@ -931,8 +917,7 @@
       // If the URI cannot be resolved, for example because the factory
       // does not understand the scheme, return the unresolved file instance.
       if (uriSource == null) {
-        _uriToFile[uri] = unresolvedFile;
-        return unresolvedFile;
+        return null;
       }
 
       String path = uriSource.fullName;
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index e6ca405..cf8be4e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -548,7 +548,8 @@
     LibraryIdentifier libraryNameNode;
     var seenPartSources = <Source>{};
     var directivesToResolve = <Directive>[];
-    int partIndex = 0;
+    int partDirectiveIndex = 0;
+    int partElementIndex = 0;
     for (Directive directive in definingCompilationUnit.directives) {
       if (directive is LibraryDirective) {
         libraryNameNode = directive.name;
@@ -583,12 +584,15 @@
       } else if (directive is PartDirective) {
         StringLiteral partUri = directive.uri;
 
-        FileState partFile = _library.partedFiles[partIndex];
-        CompilationUnit partUnit = units[partFile];
-        CompilationUnitElement partElement = _libraryElement.parts[partIndex];
+        FileState partFile = _library.partedFiles[partDirectiveIndex++];
+        if (partFile == null) {
+          continue;
+        }
+
+        var partUnit = units[partFile];
+        var partElement = _libraryElement.parts[partElementIndex++];
         partUnit.element = partElement;
         directive.element = partElement;
-        partIndex++;
 
         Source partSource = directive.uriSource;
         if (partSource == null) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 82342fd..411150a 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -119,10 +119,6 @@
 
       librariesTotal += cycle.libraries.length;
 
-      if (cycle.isUnresolvedFile) {
-        return;
-      }
-
       cycle.directDependencies.forEach(
         (e) => loadBundle(e, '$debugPrefix  '),
       );
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index bb04579..08cb6aa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -8,6 +8,7 @@
 import 'package:analyzer/src/summary/api_signature.dart';
 import 'package:analyzer/src/summary/link.dart' as graph
     show DependencyWalker, Node;
+import 'package:collection/collection.dart';
 
 /// Ensure that the [FileState.libraryCycle] for the [file] and anything it
 /// depends on is computed.
@@ -51,10 +52,6 @@
 
   LibraryCycle.external() : transitiveSignature = '<external>';
 
-  bool get isUnresolvedFile {
-    return libraries.length == 1 && libraries[0].isUnresolved;
-  }
-
   /// Invalidate this cycle and any cycles that directly or indirectly use it.
   ///
   /// Practically invalidation means that we clear the library cycle in all the
@@ -121,8 +118,10 @@
     // Append direct referenced cycles.
     for (var node in scc) {
       var file = node.file;
-      _appendDirectlyReferenced(cycle, signature, file.importedFiles);
-      _appendDirectlyReferenced(cycle, signature, file.exportedFiles);
+      _appendDirectlyReferenced(
+          cycle, signature, file.importedFiles.whereNotNull().toList());
+      _appendDirectlyReferenced(
+          cycle, signature, file.exportedFiles.whereNotNull().toList());
     }
 
     // Fill the cycle with libraries.
diff --git a/pkg/analyzer/lib/src/dart/constant/utilities.dart b/pkg/analyzer/lib/src/dart/constant/utilities.dart
index 72e220c..077ccf5 100644
--- a/pkg/analyzer/lib/src/dart/constant/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/constant/utilities.dart
@@ -217,7 +217,8 @@
     if (elementAnnotation == null) {
       // Analyzer ignores annotations on "part of" directives and on enum
       // constant declarations.
-      assert(node.parent is PartOfDirective ||
+      assert(node.parent is PartDirective ||
+          node.parent is PartOfDirective ||
           node.parent is EnumConstantDeclaration);
     } else {
       constantsToCompute.add(elementAnnotation);
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index bd112a8..274b269 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -37,7 +37,7 @@
     ElementAnnotationImpl elementAnnotationImpl = node.elementAnnotation;
     if (elementAnnotationImpl == null) {
       // Analyzer ignores annotations on "part of" directives.
-      assert(parent is PartOfDirective);
+      assert(parent is PartDirective || parent is PartOfDirective);
     } else {
       elementAnnotationImpl.annotationAst = _createCloner().cloneNode(node);
     }
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 54fb25c..eba129d 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -741,7 +741,9 @@
     for (Annotation annotation in annotations) {
       var elementAnnotation =
           annotation.elementAnnotation as ElementAnnotationImpl;
-      elementAnnotation.element = annotation.element;
+      if (elementAnnotation != null) {
+        elementAnnotation.element = annotation.element;
+      }
     }
   }
 }
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index b61f2fb..a07adc0 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -10,7 +10,7 @@
   _fe_analyzer_shared: ^14.0.0
   args: ^1.0.0
   cli_util: '>=0.1.4 <0.3.0'
-  collection: ^1.10.1
+  collection: ^1.15.0-nullsafety.5
   convert: ^2.0.0
   crypto: ^2.0.0
   glob: '>=1.0.3 <3.0.0'
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index bcea8e7..56774cd 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -283,11 +283,9 @@
 part 'not_dart.txt';
 ''');
     FileState file = fileSystemState.getFileForPath(a);
-    expect(_excludeSdk(file.importedFiles).map((f) => f.path),
-        unorderedEquals([b, not_dart]));
-    expect(
-        file.exportedFiles.map((f) => f.path), unorderedEquals([c, not_dart]));
-    expect(file.partedFiles.map((f) => f.path), unorderedEquals([d, not_dart]));
+    expect(_excludeSdk(file.importedFiles).map((f) => f.path), [b, not_dart]);
+    expect(file.exportedFiles.map((f) => f.path), [c, not_dart]);
+    expect(file.partedFiles.map((f) => f.path), [d, not_dart]);
     expect(_excludeSdk(fileSystemState.knownFilePaths),
         unorderedEquals([a, b, c, d, not_dart]));
   }
@@ -350,10 +348,7 @@
   test_getFileForUri_invalidUri() {
     var uri = Uri.parse('package:x');
     var file = fileSystemState.getFileForUri(uri);
-    expect(file.isUnresolved, isTrue);
-    expect(file.uri, isNull);
-    expect(file.path, isNull);
-    expect(file.isPart, isFalse);
+    expect(file, isNull);
   }
 
   test_getFileForUri_packageVsFileUri() {
@@ -671,9 +666,7 @@
   }
 
   void _assertIsUnresolvedFile(FileState file) {
-    expect(file.path, isNull);
-    expect(file.uri, isNull);
-    expect(file.source, isNull);
+    expect(file, isNull);
   }
 
   void _assertLibraryCycle(
@@ -694,6 +687,8 @@
         return !file.libraries.any((file) => file.uri.isScheme('dart'));
       } else if (file is FileState) {
         return file.uri?.scheme != 'dart';
+      } else if (file == null) {
+        return true;
       } else {
         return !(file as String).startsWith(convertPath('/sdk'));
       }
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index ec2ad2b..c015730 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -313,10 +313,6 @@
 
             // Add empty synthetic units for unresolved `part` URIs.
             if (partSource == null) {
-              var unit = analysisDriver.fsState.unresolvedFile.parse();
-              inputUnits.add(
-                LinkInputUnit(partUri, null, true, unit),
-              );
               continue;
             }
 
diff --git a/pkg/analyzer_cli/test/driver_test.dart b/pkg/analyzer_cli/test/driver_test.dart
index 0ec7a94..61ea617 100644
--- a/pkg/analyzer_cli/test/driver_test.dart
+++ b/pkg/analyzer_cli/test/driver_test.dart
@@ -459,7 +459,7 @@
       var bytes = File(aSum).readAsBytesSync();
       var bundle = PackageBundleReader(bytes);
       expect(_linkedLibraryUriList(bundle), [aUri]);
-      expect(_linkedLibraryUnitUriList(bundle, aUri), [aUri, '']);
+      expect(_linkedLibraryUnitUriList(bundle, aUri), [aUri]);
     });
   }
 
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 148b6cd..4282ec2 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -37,6 +37,7 @@
 import '../ir/util.dart';
 import '../js_backend/annotations.dart';
 import '../js_backend/native_data.dart';
+import '../kernel/dart2js_target.dart' show allowedNativeTest;
 import '../kernel/element_map_impl.dart';
 import '../kernel/env.dart';
 import '../kernel/kelements.dart';
@@ -1321,18 +1322,6 @@
 
   TypeLookup _typeLookup({bool resolveAsRaw: true}) {
     bool cachedMayLookupInMain;
-    bool mayLookupInMain() {
-      var mainUri = elementEnvironment.mainLibrary.canonicalUri;
-      // Tests permit lookup outside of dart: libraries.
-      return mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js/internal')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js/native')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js_2/internal')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js_2/native'));
-    }
 
     DartType lookup(String typeName, {bool required}) {
       DartType findInLibrary(LibraryEntity library) {
@@ -1355,8 +1344,11 @@
       // TODO(johnniwinther): Narrow the set of lookups based on the depending
       // library.
       // TODO(johnniwinther): Cache more results to avoid redundant lookups?
+      cachedMayLookupInMain ??=
+          // Tests permit lookup outside of dart: libraries.
+          allowedNativeTest(elementEnvironment.mainLibrary.canonicalUri);
       DartType type;
-      if (cachedMayLookupInMain ??= mayLookupInMain()) {
+      if (cachedMayLookupInMain) {
         type ??= findInLibrary(elementEnvironment.mainLibrary);
       }
       type ??= findIn(Uris.dart_core);
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index df9dbd4..7af0873 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -9,6 +9,7 @@
 import 'package:_fe_analyzer_shared/src/messages/codes.dart'
     show Message, LocatedMessage;
 import 'package:_js_interop_checks/js_interop_checks.dart';
+import 'package:_js_interop_checks/src/js_interop.dart';
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart';
 import 'package:kernel/core_types.dart';
@@ -33,25 +34,27 @@
   'web_sql'
 ];
 
-bool maybeEnableNative(Uri uri) {
-  bool allowedTestLibrary() {
-    String scriptName = uri.path;
-    return scriptName
-            .contains(RegExp(r'(?<!generated_)tests/dart2js/native')) ||
-        scriptName.contains(RegExp(r'(?<!generated_)tests/dart2js/internal')) ||
-        scriptName.contains('generated_tests/dart2js/native/native_test') ||
-        scriptName.contains(RegExp(r'(?<!generated_)tests/dart2js_2/native')) ||
-        scriptName
-            .contains(RegExp(r'(?<!generated_)tests/dart2js_2/internal')) ||
-        scriptName.contains('generated_tests/dart2js_2/native/native_test');
-  }
+List<Pattern> _allowedNativeTestPatterns = [
+  RegExp(r'(?<!generated_)tests/dart2js/native'),
+  RegExp(r'(?<!generated_)tests/dart2js/internal'),
+  'generated_tests/dart2js/native/native_test',
+  RegExp(r'(?<!generated_)tests/dart2js_2/native'),
+  RegExp(r'(?<!generated_)tests/dart2js_2/internal'),
+  'generated_tests/dart2js_2/native/native_test',
+];
 
+bool allowedNativeTest(Uri uri) {
+  String path = uri.path;
+  return _allowedNativeTestPatterns.any((pattern) => path.contains(pattern));
+}
+
+bool maybeEnableNative(Uri uri) {
   bool allowedDartLibrary() {
     if (uri.scheme != 'dart') return false;
     return _allowedDartSchemePaths.contains(uri.path);
   }
 
-  return allowedTestLibrary() || allowedDartLibrary();
+  return allowedNativeTest(uri) || allowedDartLibrary();
 }
 
 /// A kernel [Target] to configure the Dart Front End for dart2js.
@@ -122,10 +125,20 @@
       ReferenceFromIndex referenceFromIndex,
       {void logger(String msg),
       ChangedStructureNotifier changedStructureNotifier}) {
+    var nativeClasses = <String, ir.Class>{};
+    for (var library in component.libraries) {
+      for (var cls in library.classes) {
+        var nativeNames = getNativeNames(cls);
+        for (var nativeName in nativeNames) {
+          nativeClasses[nativeName] = cls;
+        }
+      }
+    }
     for (var library in libraries) {
       JsInteropChecks(
               coreTypes,
-              diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>)
+              diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>,
+              nativeClasses)
           .visitLibrary(library);
     }
     lowering.transformLibraries(
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 8246f0b..580cc32 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -909,18 +909,6 @@
 
   TypeLookup _typeLookup({bool resolveAsRaw: true}) {
     bool cachedMayLookupInMain;
-    bool mayLookupInMain() {
-      var mainUri = elementEnvironment.mainLibrary.canonicalUri;
-      // Tests permit lookup outside of dart: libraries.
-      return mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js/internal')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js/native')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js_2/internal')) ||
-          mainUri.path
-              .contains(RegExp(r'(?<!generated_)tests/dart2js_2/native'));
-    }
 
     DartType lookup(String typeName, {bool required}) {
       DartType findInLibrary(LibraryEntity library) {
@@ -943,8 +931,11 @@
       // TODO(johnniwinther): Narrow the set of lookups based on the depending
       // library.
       // TODO(johnniwinther): Cache more results to avoid redundant lookups?
+      cachedMayLookupInMain ??=
+          // Tests permit lookup outside of dart: libraries.
+          allowedNativeTest(elementEnvironment.mainLibrary.canonicalUri);
       DartType type;
-      if (cachedMayLookupInMain ??= mayLookupInMain()) {
+      if (cachedMayLookupInMain) {
         type ??= findInLibrary(elementEnvironment.mainLibrary);
       }
       type ??= findIn(Uris.dart_core);
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index ca82d2e..9f3e929 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -17,6 +17,7 @@
 import 'package:kernel/target/targets.dart';
 import 'package:kernel/transformations/track_widget_constructor_locations.dart';
 import 'package:_js_interop_checks/js_interop_checks.dart';
+import 'package:_js_interop_checks/src/js_interop.dart';
 
 import 'constants.dart' show DevCompilerConstantsBackend;
 import 'kernel_helpers.dart';
@@ -152,10 +153,21 @@
       ReferenceFromIndex referenceFromIndex,
       {void Function(String msg) logger,
       ChangedStructureNotifier changedStructureNotifier}) {
+    var nativeClasses = <String, Class>{};
+    for (var library in component.libraries) {
+      for (var cls in library.classes) {
+        var nativeNames = getNativeNames(cls);
+        for (var nativeName in nativeNames) {
+          nativeClasses[nativeName] = cls;
+        }
+      }
+    }
     for (var library in libraries) {
       _CovarianceTransformer(library).transform();
-      JsInteropChecks(coreTypes,
-              diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>)
+      JsInteropChecks(
+              coreTypes,
+              diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>,
+              nativeClasses)
           .visitLibrary(library);
     }
   }
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index c275e1e..afaa87e 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -488,6 +488,8 @@
 JsInteropJSClassExtendsDartClass/example: Fail # Web compiler specific
 JsInteropNamedParameters/analyzerCode: Fail # Web compiler specific
 JsInteropNamedParameters/example: Fail # Web compiler specific
+JsInteropNativeClassInAnnotation/analyzerCode: Fail # Web compiler specific
+JsInteropNativeClassInAnnotation/example: Fail # Web compiler specific
 JsInteropNonExternalConstructor/analyzerCode: Fail # Web compiler specific
 JsInteropNonExternalConstructor/example: Fail # Web compiler specific
 JsInteropNonExternalMember/analyzerCode: Fail # Web compiler specific
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 2bcb9d4..9c849fb 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4666,6 +4666,9 @@
   template: "Named parameters for JS interop functions are only allowed in a factory constructor of an @anonymous JS class."
   tip: "Try replacing them with normal or optional parameters."
 
+JsInteropNativeClassInAnnotation:
+  template: "JS interop class '#name' conflicts with natively supported class '#name2' in '#string3'."
+
 JsInteropNonExternalConstructor:
   template: "JS interop classes do not support non-external constructors."
   tip: "Try annotating with `external`."
@@ -4927,4 +4930,4 @@
   exampleAllowMoreCodes: true
   analyzerCode: UNEXPECTED_TOKEN
   script:
-    - "late int x;"
\ No newline at end of file
+    - "late int x;"
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index debea18..d926797 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -40,6 +40,7 @@
 name.stack
 nameokempty
 native('native
+natively
 nativetype
 nnbd
 nosuchmethod
diff --git a/tests/language/identifier/built_in_illegal_test.dart b/tests/language/identifier/built_in_illegal_test.dart
index 86d9e5c..983a854 100644
--- a/tests/language/identifier/built_in_illegal_test.dart
+++ b/tests/language/identifier/built_in_illegal_test.dart
@@ -131,7 +131,6 @@
 // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
 // [cfe] Expected an identifier, but got 'part'.
 // [error line 123, column 12, length 0]
-// [analyzer] COMPILE_TIME_ERROR.PART_OF_NON_PART
 // [cfe] Expected ';' after this.
 //         ^
 // [analyzer] SYNTACTIC_ERROR.EXPECTED_EXECUTABLE
diff --git a/tests/language_2/identifier/built_in_illegal_test.dart b/tests/language_2/identifier/built_in_illegal_test.dart
index 86d9e5c..983a854 100644
--- a/tests/language_2/identifier/built_in_illegal_test.dart
+++ b/tests/language_2/identifier/built_in_illegal_test.dart
@@ -131,7 +131,6 @@
 // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
 // [cfe] Expected an identifier, but got 'part'.
 // [error line 123, column 12, length 0]
-// [analyzer] COMPILE_TIME_ERROR.PART_OF_NON_PART
 // [cfe] Expected ';' after this.
 //         ^
 // [analyzer] SYNTACTIC_ERROR.EXPECTED_EXECUTABLE
diff --git a/tests/lib/html/js_interop_constructor_name/div_test.dart b/tests/lib/html/js_interop_constructor_name/div_test.dart
index 02a7826..a61ae6f 100644
--- a/tests/lib/html/js_interop_constructor_name/div_test.dart
+++ b/tests/lib/html/js_interop_constructor_name/div_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('dom-is-dom', () {
     var e = confuse(new html.DivElement());
     expect(e is html.DivElement, isTrue);
diff --git a/tests/lib/html/js_interop_constructor_name/div_test.html b/tests/lib/html/js_interop_constructor_name/div_test.html
deleted file mode 100644
index 7ef030a..0000000
--- a/tests/lib/html/js_interop_constructor_name/div_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> div_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running div_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib/html/js_interop_constructor_name/error1_test.dart b/tests/lib/html/js_interop_constructor_name/error1_test.dart
index bb27475..a12be51 100644
--- a/tests/lib/html/js_interop_constructor_name/error1_test.dart
+++ b/tests/lib/html/js_interop_constructor_name/error1_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('dom-is-js', () {
     var e = confuse(new html.DivElement());
     // Currently, HTML types are not [JavaScriptObject]s. We could change that
diff --git a/tests/lib/html/js_interop_constructor_name/error1_test.html b/tests/lib/html/js_interop_constructor_name/error1_test.html
deleted file mode 100644
index 7eb4631..0000000
--- a/tests/lib/html/js_interop_constructor_name/error1_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> error1_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running error1_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib/html/js_interop_constructor_name/error2_test.dart b/tests/lib/html/js_interop_constructor_name/error2_test.dart
index 9fab532..365e418 100644
--- a/tests/lib/html/js_interop_constructor_name/error2_test.dart
+++ b/tests/lib/html/js_interop_constructor_name/error2_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('String-is-not-js', () {
     var e = confuse('kombucha');
     // A String should not be a JS interop type. The type test flags are added
diff --git a/tests/lib/html/js_interop_constructor_name/error2_test.html b/tests/lib/html/js_interop_constructor_name/error2_test.html
deleted file mode 100644
index f05cdb4..0000000
--- a/tests/lib/html/js_interop_constructor_name/error2_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> error2_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running error2_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib/html/js_interop_constructor_name/method_test.dart b/tests/lib/html/js_interop_constructor_name/method_test.dart
index e881467..9781829 100644
--- a/tests/lib/html/js_interop_constructor_name/method_test.dart
+++ b/tests/lib/html/js_interop_constructor_name/method_test.dart
@@ -2,29 +2,15 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic, Expect;
+import 'package:expect/expect.dart' show Expect;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('js-call-js-method', () {
     var e = confuse(makeDiv('hello'));
     expect(e.bar(), equals('hello'));
diff --git a/tests/lib/html/js_interop_constructor_name/method_test.html b/tests/lib/html/js_interop_constructor_name/method_test.html
deleted file mode 100644
index a8a0173..0000000
--- a/tests/lib/html/js_interop_constructor_name/method_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> method_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running method_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib/html/js_interop_constructor_name/test_js.js b/tests/lib/html/js_interop_constructor_name/test_js.js
deleted file mode 100644
index 828cbc9..0000000
--- a/tests/lib/html/js_interop_constructor_name/test_js.js
+++ /dev/null
@@ -1,20 +0,0 @@
-(function() {
-
-  // A constructor function with the same name as a HTML element.
-  function HTMLDivElement(a) {
-    this.a = a;
-  }
-
-  HTMLDivElement.prototype.bar = function() {
-    return this.a;
-  }
-
-  HTMLDivElement.prototype.toString = function() {
-    return "HTMLDivElement(" + this.a + ")";
-  }
-
-  self.makeDiv = function(text) {
-    return new HTMLDivElement(text);
-  };
-
-})();
diff --git a/tests/lib/html/js_interop_constructor_name/util.dart b/tests/lib/html/js_interop_constructor_name/util.dart
new file mode 100644
index 0000000..03659f0
--- /dev/null
+++ b/tests/lib/html/js_interop_constructor_name/util.dart
@@ -0,0 +1,49 @@
+// 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.
+
+@JS()
+library util;
+
+import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
+import 'package:js/js.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external makeDiv(String text);
+
+// Static error to name @JS class the same as a @Native class, so we use a
+// namespace `Foo` to avoid conflicting with the native class.
+@JS('Foo.HTMLDivElement')
+class HTMLDivElement {
+  external String bar();
+}
+
+@pragma('dart2js:noInline')
+@pragma('dart2js:assumeDynamic')
+confuse(x) => x;
+
+void setUpJS() {
+  eval(r"""
+  var Foo = {}
+
+  // A constructor function with the same name as a HTML element.
+  Foo.HTMLDivElement = function(a) {
+    this.a = a;
+  }
+
+  Foo.HTMLDivElement.prototype.bar = function() {
+    return this.a;
+  }
+
+  Foo.HTMLDivElement.prototype.toString = function() {
+    return "HTMLDivElement(" + this.a + ")";
+  }
+
+  self.makeDiv = function(text) {
+    return new Foo.HTMLDivElement(text);
+  }
+  """);
+}
diff --git a/tests/lib/js/native_as_js_classes_static_test/default_library_namespace_test.dart b/tests/lib/js/native_as_js_classes_static_test/default_library_namespace_test.dart
new file mode 100644
index 0000000..d230129
--- /dev/null
+++ b/tests/lib/js/native_as_js_classes_static_test/default_library_namespace_test.dart
@@ -0,0 +1,75 @@
+// 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.
+
+// Test errors with a library with the default namespace.
+
+@JS()
+library default_library_namespace_test;
+
+import 'package:js/js.dart';
+
+// Test same class name as a native class.
+@JS()
+class HTMLDocument {}
+//    ^
+// [web] JS interop class 'HTMLDocument' conflicts with natively supported class 'HtmlDocument' in 'dart:html'.
+
+// Test same annotation name as a native class.
+@JS('HTMLDocument')
+class HtmlDocument {}
+//    ^
+// [web] JS interop class 'HtmlDocument' conflicts with natively supported class 'HtmlDocument' in 'dart:html'.
+
+// Test annotation name with 'self' and 'window' prefixes.
+@JS('self.Window')
+class WindowWithSelf {}
+//    ^
+// [web] JS interop class 'WindowWithSelf' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('window.Window')
+class WindowWithWindow {}
+//    ^
+// [web] JS interop class 'WindowWithWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('self.window.self.window.self.Window')
+class WindowWithMultipleSelfsAndWindows {}
+//    ^
+// [web] JS interop class 'WindowWithMultipleSelfsAndWindows' conflicts with natively supported class 'Window' in 'dart:html'.
+
+// Test annotation with native class name but with a prefix that isn't 'self' or
+// 'window'.
+@JS('foo.Window')
+class WindowWithDifferentPrefix {}
+
+// Test same class name as a native class with multiple annotation names.
+// dart:html.Window uses both "Window" and "DOMWindow".
+@JS()
+class DOMWindow {}
+//    ^
+// [web] JS interop class 'DOMWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+// Test same annotation name as a native class with multiple annotation names
+// dart:html.Window uses both "Window" and "DOMWindow".
+@JS('DOMWindow')
+class DomWindow {}
+//    ^
+// [web] JS interop class 'DomWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+// Test different annotation name but with same class name as a @Native class.
+@JS('Foo')
+class Window {}
+
+// Dart classes don't have to worry about conflicts.
+class Element {}
+
+// Anonymous classes don't have to worry about conflicts either.
+@JS()
+@anonymous
+class HTMLElement {}
+
+@JS('HTMLElement')
+@anonymous
+class HtmlElement {}
+
+void main() {}
diff --git a/tests/lib/js/native_as_js_classes_static_test/global_library_namespace_test.dart b/tests/lib/js/native_as_js_classes_static_test/global_library_namespace_test.dart
new file mode 100644
index 0000000..fb55aa6
--- /dev/null
+++ b/tests/lib/js/native_as_js_classes_static_test/global_library_namespace_test.dart
@@ -0,0 +1,63 @@
+// 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.
+
+// Test errors with a library with a global namespace.
+
+@JS('window')
+library global_library_namespace_test;
+
+import 'package:js/js.dart';
+
+@JS()
+class HTMLDocument {}
+//    ^
+// [web] JS interop class 'HTMLDocument' conflicts with natively supported class 'HtmlDocument' in 'dart:html'.
+
+@JS('HTMLDocument')
+class HtmlDocument {}
+//    ^
+// [web] JS interop class 'HtmlDocument' conflicts with natively supported class 'HtmlDocument' in 'dart:html'.
+
+@JS('self.Window')
+class WindowWithSelf {}
+//    ^
+// [web] JS interop class 'WindowWithSelf' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('window.Window')
+class WindowWithWindow {}
+//    ^
+// [web] JS interop class 'WindowWithWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('self.window.self.window.self.Window')
+class WindowWithMultipleSelfsAndWindows {}
+//    ^
+// [web] JS interop class 'WindowWithMultipleSelfsAndWindows' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('foo.Window')
+class WindowWithDifferentPrefix {}
+
+@JS()
+class DOMWindow {}
+//    ^
+// [web] JS interop class 'DOMWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('DOMWindow')
+class DomWindow {}
+//    ^
+// [web] JS interop class 'DomWindow' conflicts with natively supported class 'Window' in 'dart:html'.
+
+@JS('Foo')
+class Window {}
+
+class Element {}
+
+@JS()
+@anonymous
+class HTMLElement {}
+
+@JS('HTMLElement')
+@anonymous
+class HtmlElement {}
+
+void main() {}
diff --git a/tests/lib/js/native_as_js_classes_static_test/local_library_namespace_test.dart b/tests/lib/js/native_as_js_classes_static_test/local_library_namespace_test.dart
new file mode 100644
index 0000000..e529bfa
--- /dev/null
+++ b/tests/lib/js/native_as_js_classes_static_test/local_library_namespace_test.dart
@@ -0,0 +1,50 @@
+// 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.
+
+// Test errors with a library with a non-default or non-global namespace.
+// Note that none of the following should be errors in this case.
+
+@JS('foo')
+library global_library_namespace_test;
+
+import 'package:js/js.dart';
+
+@JS()
+class HTMLDocument {}
+
+@JS('HTMLDocument')
+class HtmlDocument {}
+
+@JS('self.Window')
+class WindowWithSelf {}
+
+@JS('window.Window')
+class WindowWithWindow {}
+
+@JS('self.window.self.window.self.Window')
+class WindowWithMultipleSelfsAndWindows {}
+
+@JS('foo.Window')
+class WindowWithDifferentPrefix {}
+
+@JS()
+class DOMWindow {}
+
+@JS('DOMWindow')
+class DomWindow {}
+
+@JS('Foo')
+class Window {}
+
+class Element {}
+
+@JS()
+@anonymous
+class HTMLElement {}
+
+@JS('HTMLElement')
+@anonymous
+class HtmlElement {}
+
+void main() {}
diff --git a/tests/lib_2/html/js_interop_constructor_name/div_test.dart b/tests/lib_2/html/js_interop_constructor_name/div_test.dart
index 02a7826..a61ae6f 100644
--- a/tests/lib_2/html/js_interop_constructor_name/div_test.dart
+++ b/tests/lib_2/html/js_interop_constructor_name/div_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('dom-is-dom', () {
     var e = confuse(new html.DivElement());
     expect(e is html.DivElement, isTrue);
diff --git a/tests/lib_2/html/js_interop_constructor_name/div_test.html b/tests/lib_2/html/js_interop_constructor_name/div_test.html
deleted file mode 100644
index 31aad5e..0000000
--- a/tests/lib_2/html/js_interop_constructor_name/div_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> div_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running div_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib_2/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib_2/html/js_interop_constructor_name/error1_test.dart b/tests/lib_2/html/js_interop_constructor_name/error1_test.dart
index bb27475..a12be51 100644
--- a/tests/lib_2/html/js_interop_constructor_name/error1_test.dart
+++ b/tests/lib_2/html/js_interop_constructor_name/error1_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('dom-is-js', () {
     var e = confuse(new html.DivElement());
     // Currently, HTML types are not [JavaScriptObject]s. We could change that
diff --git a/tests/lib_2/html/js_interop_constructor_name/error1_test.html b/tests/lib_2/html/js_interop_constructor_name/error1_test.html
deleted file mode 100644
index 9c6566e..0000000
--- a/tests/lib_2/html/js_interop_constructor_name/error1_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> error1_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running error1_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib_2/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib_2/html/js_interop_constructor_name/error2_test.dart b/tests/lib_2/html/js_interop_constructor_name/error2_test.dart
index 9fab532..365e418 100644
--- a/tests/lib_2/html/js_interop_constructor_name/error2_test.dart
+++ b/tests/lib_2/html/js_interop_constructor_name/error2_test.dart
@@ -2,29 +2,14 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('String-is-not-js', () {
     var e = confuse('kombucha');
     // A String should not be a JS interop type. The type test flags are added
diff --git a/tests/lib_2/html/js_interop_constructor_name/error2_test.html b/tests/lib_2/html/js_interop_constructor_name/error2_test.html
deleted file mode 100644
index 7e1b64f..0000000
--- a/tests/lib_2/html/js_interop_constructor_name/error2_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> error2_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running error2_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib_2/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib_2/html/js_interop_constructor_name/method_test.dart b/tests/lib_2/html/js_interop_constructor_name/method_test.dart
index e881467..9781829 100644
--- a/tests/lib_2/html/js_interop_constructor_name/method_test.dart
+++ b/tests/lib_2/html/js_interop_constructor_name/method_test.dart
@@ -2,29 +2,15 @@
 // 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.
 
-library jsTest;
-
-import 'dart:async';
 import 'dart:html' as html;
-import 'dart:js';
-import 'package:js/js.dart';
 
-import 'package:expect/expect.dart' show NoInline, AssumeDynamic, Expect;
+import 'package:expect/expect.dart' show Expect;
 import 'package:expect/minitest.dart';
 
-@JS()
-external makeDiv(String text);
-
-@JS()
-class HTMLDivElement {
-  external String bar();
-}
-
-@pragma('dart2js:noInline')
-@pragma('dart2js:assumeDynamic')
-confuse(x) => x;
+import 'util.dart';
 
 main() {
+  setUpJS();
   test('js-call-js-method', () {
     var e = confuse(makeDiv('hello'));
     expect(e.bar(), equals('hello'));
diff --git a/tests/lib_2/html/js_interop_constructor_name/method_test.html b/tests/lib_2/html/js_interop_constructor_name/method_test.html
deleted file mode 100644
index c38d07dd..0000000
--- a/tests/lib_2/html/js_interop_constructor_name/method_test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="dart.unittest" content="full-stack-traces">
-  <title> method_test </title>
-  <style>
-     .unittest-table { font-family:monospace; border:1px; }
-     .unittest-pass { background: #6b3;}
-     .unittest-fail { background: #d55;}
-     .unittest-error { background: #a11;}
-  </style>
-</head>
-<body>
-  <h1> Running method_test </h1>
-  <script type="text/javascript"
-      src="/root_dart/tests/lib_2/html/js_interop_constructor_name/test_js.js"></script>
-  <script type="text/javascript"
-      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
-  %TEST_SCRIPTS%
-</body>
-</html>
diff --git a/tests/lib_2/html/js_interop_constructor_name/test_js.js b/tests/lib_2/html/js_interop_constructor_name/test_js.js
deleted file mode 100644
index 828cbc9..0000000
--- a/tests/lib_2/html/js_interop_constructor_name/test_js.js
+++ /dev/null
@@ -1,20 +0,0 @@
-(function() {
-
-  // A constructor function with the same name as a HTML element.
-  function HTMLDivElement(a) {
-    this.a = a;
-  }
-
-  HTMLDivElement.prototype.bar = function() {
-    return this.a;
-  }
-
-  HTMLDivElement.prototype.toString = function() {
-    return "HTMLDivElement(" + this.a + ")";
-  }
-
-  self.makeDiv = function(text) {
-    return new HTMLDivElement(text);
-  };
-
-})();
diff --git a/tests/lib_2/html/js_interop_constructor_name/util.dart b/tests/lib_2/html/js_interop_constructor_name/util.dart
new file mode 100644
index 0000000..03659f0
--- /dev/null
+++ b/tests/lib_2/html/js_interop_constructor_name/util.dart
@@ -0,0 +1,49 @@
+// 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.
+
+@JS()
+library util;
+
+import 'package:expect/expect.dart' show NoInline, AssumeDynamic;
+import 'package:js/js.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external makeDiv(String text);
+
+// Static error to name @JS class the same as a @Native class, so we use a
+// namespace `Foo` to avoid conflicting with the native class.
+@JS('Foo.HTMLDivElement')
+class HTMLDivElement {
+  external String bar();
+}
+
+@pragma('dart2js:noInline')
+@pragma('dart2js:assumeDynamic')
+confuse(x) => x;
+
+void setUpJS() {
+  eval(r"""
+  var Foo = {}
+
+  // A constructor function with the same name as a HTML element.
+  Foo.HTMLDivElement = function(a) {
+    this.a = a;
+  }
+
+  Foo.HTMLDivElement.prototype.bar = function() {
+    return this.a;
+  }
+
+  Foo.HTMLDivElement.prototype.toString = function() {
+    return "HTMLDivElement(" + this.a + ")";
+  }
+
+  self.makeDiv = function(text) {
+    return new Foo.HTMLDivElement(text);
+  }
+  """);
+}
diff --git a/tools/VERSION b/tools/VERSION
index ef0bcd0..7bed7b7 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 178
+PRERELEASE 179
 PRERELEASE_PATCH 0
\ No newline at end of file