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