Version 2.17.0-256.0.dev
Merge commit 'b55dc762fa8858d747759bba29900891cb2ecc68' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9280fed..3e7b432 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -233,8 +233,16 @@
tracker.
`dart --verbose pub [command]` will also cause the log file to be written.
+- `dart pub global activate --source=git` now takes arguments `--git-path` to
+ specify the path of the activated package in the pubspec and `--git-ref` to
+ specify the branch or revision to check out.
- `dart pub add` can now add multiple packages in one command.
-
+- `dart pub token add` can now add a token for [pub.dev](https://pub.dev).
+- `dart pub uploader` has been removed. To manage uploaders for a package use
+ the `https://pub.dev/<packagename>/admin` web-interface.
+- Pub now supports a separate `pubspec_overrides.yaml` file that can contain
+ `dependency_overrides`. This makes it easier to avoid checking the local
+ overrides into version control.
#### Linter
Updated the Linter to `1.18.0`, which includes changes that
diff --git a/DEPS b/DEPS
index 3dce774..9d89269 100644
--- a/DEPS
+++ b/DEPS
@@ -141,7 +141,7 @@
"pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
"process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
"protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
- "pub_rev": "8f5ab7b1aba3b9f66b56246d77e167990339d317",
+ "pub_rev": "a3a102a549388a6dbfecc9252fabb618f9a2f5f7",
"pub_semver_rev": "ea6c54019948dc03042c595ce9413e17aaf7aa38",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
diff --git a/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart b/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart
new file mode 100644
index 0000000..7969da6
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart
@@ -0,0 +1,305 @@
+// Copyright (c) 2022, 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.
+
+// Micro-benchmark for testing async/await performance in presence of
+// different number of live values across await.
+
+import 'dart:async';
+
+import 'async_benchmark_base.dart' show AsyncBenchmarkBase;
+
+class MockClass {
+ static final String str = "${int.parse('42')}";
+ static final List<int> list =
+ List<int>.filled(int.parse('3'), int.parse('42'));
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ String get1() => str;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ List<int> get2() => list;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use1(String a0) => a0.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use2(String a0, List<int> a1) => a0.length + a1.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use4(String a0, List<int> a1, String a2, List<int> a3) =>
+ a0.length + a1.length + a2.length + a3.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
+ List<int> a5, String a6, List<int> a7) =>
+ a0.length +
+ a1.length +
+ a2.length +
+ a3.length +
+ a4.length +
+ a5.length +
+ a6.length +
+ a7.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ Future<void> asyncMethod() async {}
+}
+
+class MockClass2 {
+ static int val1 = int.parse('42');
+ static int val2 = int.parse('43');
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ int get1() => val1;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ int get2() => val2;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use1(int a0) => a0;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use2(int a0, int a1) => a0 + a1;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ Future<void> asyncMethod() async {}
+}
+
+class LiveVarsBench extends AsyncBenchmarkBase {
+ LiveVarsBench(String name) : super(name);
+ @override
+ Future<void> exercise() async {
+ // These micro-benchmarks are too small, so
+ // make a larger number of iterations per measurement.
+ for (var i = 0; i < 10000; i++) {
+ await run();
+ }
+ }
+}
+
+class LiveObj1 extends LiveVarsBench {
+ LiveObj1() : super('AsyncLiveVars.LiveObj1');
+ final field1 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ }
+}
+
+class LiveObj2 extends LiveVarsBench {
+ LiveObj2() : super('AsyncLiveVars.LiveObj2');
+ final field1 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use2(obj1, obj2);
+ }
+}
+
+class LiveObj4 extends LiveVarsBench {
+ LiveObj4() : super('AsyncLiveVars.LiveObj4');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field2.use1(obj3);
+ await field2.asyncMethod();
+ field1.use4(obj1, obj2, obj3, obj4);
+ }
+}
+
+class LiveObj8 extends LiveVarsBench {
+ LiveObj8() : super('AsyncLiveVars.LiveObj8');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass();
+ final field4 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final obj5 = field3.get1();
+ final obj6 = field3.get2();
+ final obj7 = field4.get1();
+ final obj8 = field4.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field3.use2(obj5, obj6);
+ await field4.asyncMethod();
+ field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+ }
+}
+
+class LiveObj16 extends LiveVarsBench {
+ LiveObj16() : super('AsyncLiveVars.LiveObj16');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass();
+ final field4 = MockClass();
+ final field5 = MockClass();
+ final field6 = MockClass();
+ final field7 = MockClass();
+ final field8 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final obj5 = field3.get1();
+ final obj6 = field3.get2();
+ final obj7 = field4.get1();
+ final obj8 = field4.get2();
+ final obj9 = field5.get1();
+ final obj10 = field5.get2();
+ final obj11 = field6.get1();
+ final obj12 = field6.get2();
+ final obj13 = field7.get1();
+ final obj14 = field7.get2();
+ final obj15 = field8.get1();
+ final obj16 = field8.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field5.use2(obj11, obj12);
+ await field4.asyncMethod();
+ field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+ field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
+ }
+}
+
+class LiveInt1 extends LiveVarsBench {
+ LiveInt1() : super('AsyncLiveVars.LiveInt1');
+ final field1 = MockClass2();
+ @override
+ Future<void> run() async {
+ final int1 = field1.get1();
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field1.use1(int1);
+ }
+}
+
+class LiveInt4 extends LiveVarsBench {
+ LiveInt4() : super('AsyncLiveVars.LiveInt4');
+ final field1 = MockClass2();
+ final field2 = MockClass2();
+ @override
+ Future<void> run() async {
+ final int1 = field1.get1();
+ final int2 = field1.get2();
+ final int3 = field2.get1();
+ final int4 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field2.use1(int3);
+ await field2.asyncMethod();
+ field1.use4(int1, int2, int3, int4);
+ }
+}
+
+class LiveObj2Int2 extends LiveVarsBench {
+ LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
+ final field1 = MockClass();
+ final field2 = MockClass2();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final int1 = field2.get1();
+ final int2 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field2.use1(int1);
+ await field2.asyncMethod();
+ field1.use2(obj1, obj2);
+ field2.use2(int1, int2);
+ }
+}
+
+class LiveObj4Int4 extends LiveVarsBench {
+ LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass2();
+ final field4 = MockClass2();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final int1 = field3.get1();
+ final int2 = field3.get2();
+ final int3 = field4.get1();
+ final int4 = field4.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field3.use2(int2, int4);
+ await field4.asyncMethod();
+ field2.use4(obj1, obj2, obj3, obj4);
+ field4.use4(int1, int2, int3, int4);
+ }
+}
+
+Future<void> main() async {
+ final benchmarks = [
+ LiveObj1(),
+ LiveObj2(),
+ LiveObj4(),
+ LiveObj8(),
+ LiveObj16(),
+ LiveInt1(),
+ LiveInt4(),
+ LiveObj2Int2(),
+ LiveObj4Int4()
+ ];
+ for (final bench in benchmarks) {
+ await bench.report();
+ }
+}
diff --git a/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart b/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart
new file mode 100644
index 0000000..9a415b9
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:benchmark_harness/benchmark_harness.dart'
+ show PrintEmitter, ScoreEmitter;
+
+// Similar to BenchmarkBase from package:benchmark_harness.
+class AsyncBenchmarkBase {
+ final String name;
+ final ScoreEmitter emitter;
+
+ // Empty constructor.
+ const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
+
+ // The benchmark code.
+ // This function is not used, if both [warmup] and [exercise] are overwritten.
+ Future<void> run() async {}
+
+ // Runs a short version of the benchmark. By default invokes [run] once.
+ Future<void> warmup() async {
+ await run();
+ }
+
+ // Exercises the benchmark. By default invokes [run] 10 times.
+ Future<void> exercise() async {
+ for (var i = 0; i < 10; i++) {
+ await run();
+ }
+ }
+
+ // Not measured setup code executed prior to the benchmark runs.
+ Future<void> setup() async {}
+
+ // Not measures teardown code executed after the benchark runs.
+ Future<void> teardown() async {}
+
+ // Measures the score for this benchmark by executing it repeatedly until
+ // time minimum has been reached.
+ static Future<double> measureFor(Function f, int minimumMillis) async {
+ final int minimumMicros = minimumMillis * 1000;
+ int iter = 0;
+ final watch = Stopwatch();
+ watch.start();
+ int elapsed = 0;
+ while (elapsed < minimumMicros) {
+ await f();
+ elapsed = watch.elapsedMicroseconds;
+ iter++;
+ }
+ return elapsed / iter;
+ }
+
+ // Measures the score for the benchmark and returns it.
+ Future<double> measure() async {
+ await setup();
+ // Warmup for at least 100ms. Discard result.
+ await measureFor(warmup, 100);
+ // Run the benchmark for at least 2000ms.
+ final result = await measureFor(exercise, 2000);
+ await teardown();
+ return result;
+ }
+
+ Future<void> report() async {
+ emitter.emit(name, await measure());
+ }
+}
diff --git a/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart b/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart
new file mode 100644
index 0000000..9176274
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart
@@ -0,0 +1,307 @@
+// Copyright (c) 2022, 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.
+
+// Micro-benchmark for testing async/await performance in presence of
+// different number of live values across await.
+
+// @dart=2.9
+
+import 'dart:async';
+
+import 'async_benchmark_base.dart' show AsyncBenchmarkBase;
+
+class MockClass {
+ static final String str = "${int.parse('42')}";
+ static final List<int> list =
+ List<int>.filled(int.parse('3'), int.parse('42'));
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ String get1() => str;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ List<int> get2() => list;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use1(String a0) => a0.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use2(String a0, List<int> a1) => a0.length + a1.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use4(String a0, List<int> a1, String a2, List<int> a3) =>
+ a0.length + a1.length + a2.length + a3.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
+ List<int> a5, String a6, List<int> a7) =>
+ a0.length +
+ a1.length +
+ a2.length +
+ a3.length +
+ a4.length +
+ a5.length +
+ a6.length +
+ a7.length;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ Future<void> asyncMethod() async {}
+}
+
+class MockClass2 {
+ static int val1 = int.parse('42');
+ static int val2 = int.parse('43');
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ int get1() => val1;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ int get2() => val2;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use1(int a0) => a0;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use2(int a0, int a1) => a0 + a1;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
+
+ @pragma('vm:never-inline')
+ @pragma('dart2js:noInline')
+ Future<void> asyncMethod() async {}
+}
+
+class LiveVarsBench extends AsyncBenchmarkBase {
+ LiveVarsBench(String name) : super(name);
+ @override
+ Future<void> exercise() async {
+ // These micro-benchmarks are too small, so
+ // make a larger number of iterations per measurement.
+ for (var i = 0; i < 10000; i++) {
+ await run();
+ }
+ }
+}
+
+class LiveObj1 extends LiveVarsBench {
+ LiveObj1() : super('AsyncLiveVars.LiveObj1');
+ final field1 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ }
+}
+
+class LiveObj2 extends LiveVarsBench {
+ LiveObj2() : super('AsyncLiveVars.LiveObj2');
+ final field1 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field1.use2(obj1, obj2);
+ }
+}
+
+class LiveObj4 extends LiveVarsBench {
+ LiveObj4() : super('AsyncLiveVars.LiveObj4');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field2.use1(obj3);
+ await field2.asyncMethod();
+ field1.use4(obj1, obj2, obj3, obj4);
+ }
+}
+
+class LiveObj8 extends LiveVarsBench {
+ LiveObj8() : super('AsyncLiveVars.LiveObj8');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass();
+ final field4 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final obj5 = field3.get1();
+ final obj6 = field3.get2();
+ final obj7 = field4.get1();
+ final obj8 = field4.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field3.use2(obj5, obj6);
+ await field4.asyncMethod();
+ field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+ }
+}
+
+class LiveObj16 extends LiveVarsBench {
+ LiveObj16() : super('AsyncLiveVars.LiveObj16');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass();
+ final field4 = MockClass();
+ final field5 = MockClass();
+ final field6 = MockClass();
+ final field7 = MockClass();
+ final field8 = MockClass();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final obj5 = field3.get1();
+ final obj6 = field3.get2();
+ final obj7 = field4.get1();
+ final obj8 = field4.get2();
+ final obj9 = field5.get1();
+ final obj10 = field5.get2();
+ final obj11 = field6.get1();
+ final obj12 = field6.get2();
+ final obj13 = field7.get1();
+ final obj14 = field7.get2();
+ final obj15 = field8.get1();
+ final obj16 = field8.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field5.use2(obj11, obj12);
+ await field4.asyncMethod();
+ field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+ field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
+ }
+}
+
+class LiveInt1 extends LiveVarsBench {
+ LiveInt1() : super('AsyncLiveVars.LiveInt1');
+ final field1 = MockClass2();
+ @override
+ Future<void> run() async {
+ final int1 = field1.get1();
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field1.use1(int1);
+ }
+}
+
+class LiveInt4 extends LiveVarsBench {
+ LiveInt4() : super('AsyncLiveVars.LiveInt4');
+ final field1 = MockClass2();
+ final field2 = MockClass2();
+ @override
+ Future<void> run() async {
+ final int1 = field1.get1();
+ final int2 = field1.get2();
+ final int3 = field2.get1();
+ final int4 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(int1);
+ await field1.asyncMethod();
+ field2.use1(int3);
+ await field2.asyncMethod();
+ field1.use4(int1, int2, int3, int4);
+ }
+}
+
+class LiveObj2Int2 extends LiveVarsBench {
+ LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
+ final field1 = MockClass();
+ final field2 = MockClass2();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final int1 = field2.get1();
+ final int2 = field2.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field1.asyncMethod();
+ field2.use1(int1);
+ await field2.asyncMethod();
+ field1.use2(obj1, obj2);
+ field2.use2(int1, int2);
+ }
+}
+
+class LiveObj4Int4 extends LiveVarsBench {
+ LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
+ final field1 = MockClass();
+ final field2 = MockClass();
+ final field3 = MockClass2();
+ final field4 = MockClass2();
+ @override
+ Future<void> run() async {
+ final obj1 = field1.get1();
+ final obj2 = field1.get2();
+ final obj3 = field2.get1();
+ final obj4 = field2.get2();
+ final int1 = field3.get1();
+ final int2 = field3.get2();
+ final int3 = field4.get1();
+ final int4 = field4.get2();
+ await field1.asyncMethod();
+ field1.use1(obj1);
+ await field2.asyncMethod();
+ field3.use2(int2, int4);
+ await field4.asyncMethod();
+ field2.use4(obj1, obj2, obj3, obj4);
+ field4.use4(int1, int2, int3, int4);
+ }
+}
+
+Future<void> main() async {
+ final benchmarks = [
+ LiveObj1(),
+ LiveObj2(),
+ LiveObj4(),
+ LiveObj8(),
+ LiveObj16(),
+ LiveInt1(),
+ LiveInt4(),
+ LiveObj2Int2(),
+ LiveObj4Int4()
+ ];
+ for (final bench in benchmarks) {
+ await bench.report();
+ }
+}
diff --git a/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart b/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart
new file mode 100644
index 0000000..9a415b9
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:benchmark_harness/benchmark_harness.dart'
+ show PrintEmitter, ScoreEmitter;
+
+// Similar to BenchmarkBase from package:benchmark_harness.
+class AsyncBenchmarkBase {
+ final String name;
+ final ScoreEmitter emitter;
+
+ // Empty constructor.
+ const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
+
+ // The benchmark code.
+ // This function is not used, if both [warmup] and [exercise] are overwritten.
+ Future<void> run() async {}
+
+ // Runs a short version of the benchmark. By default invokes [run] once.
+ Future<void> warmup() async {
+ await run();
+ }
+
+ // Exercises the benchmark. By default invokes [run] 10 times.
+ Future<void> exercise() async {
+ for (var i = 0; i < 10; i++) {
+ await run();
+ }
+ }
+
+ // Not measured setup code executed prior to the benchmark runs.
+ Future<void> setup() async {}
+
+ // Not measures teardown code executed after the benchark runs.
+ Future<void> teardown() async {}
+
+ // Measures the score for this benchmark by executing it repeatedly until
+ // time minimum has been reached.
+ static Future<double> measureFor(Function f, int minimumMillis) async {
+ final int minimumMicros = minimumMillis * 1000;
+ int iter = 0;
+ final watch = Stopwatch();
+ watch.start();
+ int elapsed = 0;
+ while (elapsed < minimumMicros) {
+ await f();
+ elapsed = watch.elapsedMicroseconds;
+ iter++;
+ }
+ return elapsed / iter;
+ }
+
+ // Measures the score for the benchmark and returns it.
+ Future<double> measure() async {
+ await setup();
+ // Warmup for at least 100ms. Discard result.
+ await measureFor(warmup, 100);
+ // Run the benchmark for at least 2000ms.
+ final result = await measureFor(exercise, 2000);
+ await teardown();
+ return result;
+ }
+
+ Future<void> report() async {
+ emitter.emit(name, await measure());
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
new file mode 100644
index 0000000..d5f823a
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart';
+
+/// Data structure tracking the type inference dependencies between closures
+/// passed as invocation parameters.
+///
+/// [planClosureReconciliationStages] is used as part of support for
+/// https://github.com/dart-lang/language/issues/731 (improved inference for
+/// fold etc.) to choose the proper order in which to recursively analyze
+/// closures passed as invocation arguments.
+abstract class ClosureDependencies<TypeVariable, Closure> {
+ final List<_ClosureNode<Closure>> _closureNodes = [];
+
+ /// Construct a [ClosureDependencies] object that's prepared to determine the
+ /// order to resolve [closures] for a generic invocation involving the given
+ /// [typeVariables].
+ ClosureDependencies(
+ Iterable<Closure> closures, Iterable<TypeVariable> typeVariables) {
+ Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresDependingOnTypeVar =
+ {};
+ Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresConstrainingTypeVar =
+ {};
+ for (Closure closure in closures) {
+ _ClosureNode<Closure> closureNode = new _ClosureNode<Closure>(closure);
+ _closureNodes.add(closureNode);
+ for (TypeVariable v in typeVarsFreeInClosureArguments(closure)) {
+ (closuresDependingOnTypeVar[v] ??= {}).add(closureNode);
+ }
+ for (TypeVariable v in typeVarsFreeInClosureReturns(closure)) {
+ (closuresConstrainingTypeVar[v] ??= {}).add(closureNode);
+ }
+ }
+ for (TypeVariable typeVariable in typeVariables) {
+ for (_ClosureNode<Closure> closureNode
+ in closuresDependingOnTypeVar[typeVariable] ?? const {}) {
+ closureNode.dependencies
+ .addAll(closuresConstrainingTypeVar[typeVariable] ?? const {});
+ }
+ }
+ }
+
+ /// Computes the order in which to resolve the closures passed to the
+ /// constructor.
+ ///
+ /// Each entry in the returned list represents the set of closures that should
+ /// be visited during a single stage of resolution; after each stage, the
+ /// assignment of actual types to type variables should be refined.
+ ///
+ /// So, for example, if the closures in question are A, B, and C, and the
+ /// returned list is `[{A, B}, {C}]`, then first closures A and B should be
+ /// resolved, then the assignment of actual types to type variables should be
+ /// refined, and then C should be resolved, and then the final assignment of
+ /// actual types to type variables should be computed.
+ List<Set<Closure>> planClosureReconciliationStages() {
+ _DependencyWalker<Closure> walker = new _DependencyWalker<Closure>();
+ for (_ClosureNode<Closure> closureNode in _closureNodes) {
+ walker.walk(closureNode);
+ }
+ return walker.closureReconciliationStages;
+ }
+
+ /// If the type of the parameter corresponding to [closure] is a function
+ /// type, the set of type parameters referred to by the parameter types of
+ /// that parameter. If the type of the parameter is not a function type, an
+ /// empty iterable should be returned.
+ ///
+ /// Should be overridden by the client.
+ Iterable<TypeVariable> typeVarsFreeInClosureArguments(Closure closure);
+
+ /// If the type of the parameter corresponding to [closure] is a function
+ /// type, the set of type parameters referred to by the return type of that
+ /// parameter. If the type of the parameter is not a function type, the set
+ /// type parameters referred to by the type of the parameter should be
+ /// returned.
+ ///
+ /// Should be overridden by the client.
+ Iterable<TypeVariable> typeVarsFreeInClosureReturns(Closure closure);
+}
+
+/// Node type representing a single [Closure] for purposes of walking the
+/// graph of type inference dependencies among closures.
+class _ClosureNode<Closure> extends Node<_ClosureNode<Closure>> {
+ /// The [Closure] being represented by this node.
+ final Closure closure;
+
+ /// If not `null`, the index of the reconciliation stage to which this closure
+ /// has been assigned.
+ int? stageNum;
+
+ /// The nodes for the closures depended on by this closure.
+ final List<_ClosureNode<Closure>> dependencies = [];
+
+ _ClosureNode(this.closure);
+
+ @override
+ bool get isEvaluated => stageNum != null;
+
+ @override
+ List<_ClosureNode<Closure>> computeDependencies() => dependencies;
+}
+
+/// Derived class of [DependencyWalker] capable of walking the graph of type
+/// inference dependencies among closures.
+class _DependencyWalker<Closure>
+ extends DependencyWalker<_ClosureNode<Closure>> {
+ /// The set of closure reconciliation stages accumulated so far.
+ final List<Set<Closure>> closureReconciliationStages = [];
+
+ @override
+ void evaluate(_ClosureNode v) => evaluateScc([v]);
+
+ @override
+ void evaluateScc(List<_ClosureNode> nodes) {
+ int stageNum = 0;
+ for (_ClosureNode node in nodes) {
+ for (_ClosureNode dependency in node.dependencies) {
+ int? dependencyStageNum = dependency.stageNum;
+ if (dependencyStageNum != null && dependencyStageNum >= stageNum) {
+ stageNum = dependencyStageNum + 1;
+ }
+ }
+ }
+ if (closureReconciliationStages.length <= stageNum) {
+ closureReconciliationStages.add({});
+ // `stageNum` can't grow by more than 1 each time `evaluateScc` is called,
+ // so adding one stage is sufficient to make sure the list is now long
+ // enough.
+ assert(stageNum < closureReconciliationStages.length);
+ }
+ Set<Closure> stage = closureReconciliationStages[stageNum];
+ for (_ClosureNode node in nodes) {
+ node.stageNum = stageNum;
+ stage.add(node.closure);
+ }
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
index 7e2d390..382dd81 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
-import 'dart:math';
import 'dart:typed_data';
/// Collects messages from an input stream of bytes.
@@ -14,18 +13,11 @@
/// The input bytes stream subscription.
late final StreamSubscription _inputStreamSubscription;
- /// The length of the current message to read, or `-1` if we are currently
- /// reading the length.
- int _length = -1;
-
/// The buffer to store the length bytes in.
- BytesBuilder _lengthBuffer = new BytesBuilder();
+ final _FixedBuffer _lengthBuffer = new _FixedBuffer(4);
/// If reading raw data, buffer for the data.
- Uint8List _messageBuffer = new Uint8List(0);
-
- /// The position to write the next byte in [_messageBuffer].
- int _messagePos = 0;
+ _FixedBuffer? _messageBuffer;
late StreamController<Uint8List> _messageStreamController =
new StreamController<Uint8List>(onCancel: () {
@@ -38,45 +30,36 @@
}
void _handleBytes(List<int> bytes, [int offset = 0]) {
- if (_length == -1) {
- while (_lengthBuffer.length < 4 && offset < bytes.length) {
+ final _FixedBuffer? messageBuffer = _messageBuffer;
+ if (messageBuffer == null) {
+ while (offset < bytes.length && !_lengthBuffer.isReady) {
_lengthBuffer.addByte(bytes[offset++]);
}
- if (_lengthBuffer.length >= 4) {
- Uint8List lengthBytes = _lengthBuffer.takeBytes();
- _length = lengthBytes[0] << 24 |
- lengthBytes[1] << 16 |
- lengthBytes[2] << 8 |
- lengthBytes[3];
+ if (_lengthBuffer.isReady) {
+ int length = _lengthBuffer[0] << 24 |
+ _lengthBuffer[1] << 16 |
+ _lengthBuffer[2] << 8 |
+ _lengthBuffer[3];
+ // Reset the length reading state.
+ _lengthBuffer.reset();
+ // Switch to the message payload reading state.
+ _messageBuffer = new _FixedBuffer(length);
+ _handleBytes(bytes, offset);
+ } else {
+ // Continue reading the length.
+ return;
}
- }
+ } else {
+ // Read the data from `bytes`.
+ while (offset < bytes.length && !messageBuffer.isReady) {
+ messageBuffer.addByte(bytes[offset++]);
+ }
- // Just pass along `bytes` without a copy if we can, and reset our state
- if (offset == 0 && bytes.length == _length && bytes is Uint8List) {
- _length = -1;
- _messageStreamController.add(bytes);
- return;
- }
-
- // Initialize a new buffer.
- if (_messagePos == 0) {
- _messageBuffer = new Uint8List(_length);
- }
-
- // Read the data from `bytes`.
- int lenToRead = min(_length - _messagePos, bytes.length - offset);
- while (lenToRead-- > 0) {
- _messageBuffer[_messagePos++] = bytes[offset++];
- }
-
- // If we completed a message, add it to the output stream, reset our state,
- // and call ourselves again if we have more data to read.
- if (_messagePos >= _length) {
- _messageStreamController.add(_messageBuffer);
- _length = -1;
- _messagePos = 0;
-
- if (offset < bytes.length) {
+ // If we completed a message, add it to the output stream.
+ if (messageBuffer.isReady) {
+ _messageStreamController.add(messageBuffer.bytes);
+ // Switch to the length reading state.
+ _messageBuffer = null;
_handleBytes(bytes, offset);
}
}
@@ -89,3 +72,27 @@
_messageStreamController.close();
}
}
+
+/// A buffer of fixed length.
+class _FixedBuffer {
+ final Uint8List bytes;
+
+ /// The offset in [bytes].
+ int _offset = 0;
+
+ _FixedBuffer(int length) : bytes = new Uint8List(length);
+
+ /// Return `true` when the required number of bytes added.
+ bool get isReady => _offset == bytes.length;
+
+ int operator [](int index) => bytes[index];
+
+ void addByte(int byte) {
+ bytes[_offset++] = byte;
+ }
+
+ /// Reset the number of added bytes to zero.
+ void reset() {
+ _offset = 0;
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
new file mode 100644
index 0000000..ab4b8f9
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/deferred_closure_heuristic.dart';
+import 'package:test/test.dart';
+
+main() {
+ test('single', () {
+ // If there is just a single closure and no type variables, it is selected.
+ var f = Closure('f');
+ expect(
+ _TestClosureDeps(typeVars: [], closures: [f])
+ .planClosureReconciliationStages(),
+ [
+ {f}
+ ]);
+ });
+
+ test('simple dependency', () {
+ // If f depends on g, then g is selected first, and then f.
+ var f = Closure('f', argTypes: ['T']);
+ var g = Closure('g', retTypes: ['T']);
+ expect(
+ _TestClosureDeps(typeVars: ['T'], closures: [f, g])
+ .planClosureReconciliationStages(),
+ [
+ {g},
+ {f}
+ ]);
+ });
+
+ test('long chain', () {
+ // If f depends on g and g depends on h, then we do three separate stages:
+ // h, then g, then f.
+ var f = Closure('f', argTypes: ['T']);
+ var g = Closure('g', argTypes: ['U'], retTypes: ['T']);
+ var h = Closure('h', retTypes: ['U']);
+ expect(
+ _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
+ .planClosureReconciliationStages(),
+ [
+ {h},
+ {g},
+ {f}
+ ]);
+ });
+
+ test('unrelated closure', () {
+ // Closures that are independent of all the others are inferred during the
+ // first stage.
+ var f = Closure('f', argTypes: ['T']);
+ var g = Closure('g', retTypes: ['T']);
+ var h = Closure('h');
+ expect(
+ _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
+ .planClosureReconciliationStages(),
+ [
+ {g, h},
+ {f}
+ ]);
+ });
+
+ test('independent chains', () {
+ // If f depends on g, and h depends on i, then g and i are selected first,
+ // and then f and h.
+ var f = Closure('f', argTypes: ['T']);
+ var g = Closure('g', retTypes: ['T']);
+ var h = Closure('h', argTypes: ['U']);
+ var i = Closure('i', retTypes: ['U']);
+ expect(
+ _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
+ .planClosureReconciliationStages(),
+ [
+ {g, i},
+ {f, h}
+ ]);
+ });
+
+ test('diamond', () {
+ // Test a diamond dependency shape: f depends on g and h; g and h both
+ // depend on i.
+ var f = Closure('f', argTypes: ['T', 'U']);
+ var g = Closure('g', argTypes: ['V'], retTypes: ['T']);
+ var h = Closure('h', argTypes: ['V'], retTypes: ['U']);
+ var i = Closure('i', retTypes: ['V']);
+ expect(
+ _TestClosureDeps(typeVars: ['T', 'U', 'V'], closures: [f, g, h, i])
+ .planClosureReconciliationStages(),
+ [
+ {i},
+ {g, h},
+ {f}
+ ]);
+ });
+
+ test('cycle', () {
+ // A dependency cycle is inferred all at once.
+ var f = Closure('f', argTypes: ['T']);
+ var g = Closure('g', argTypes: ['U']);
+ var h = Closure('h', argTypes: ['U'], retTypes: ['T']);
+ var i = Closure('i', argTypes: ['T'], retTypes: ['U']);
+ expect(
+ _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
+ .planClosureReconciliationStages(),
+ [
+ {h, i},
+ {f, g}
+ ]);
+ });
+}
+
+class Closure {
+ final String name;
+ final List<String> argTypes;
+ final List<String> retTypes;
+
+ Closure(this.name, {this.argTypes = const [], this.retTypes = const []});
+
+ @override
+ String toString() => name;
+}
+
+class _TestClosureDeps extends ClosureDependencies<String, Closure> {
+ final List<String> typeVars;
+ final List<Closure> closures;
+
+ _TestClosureDeps({required this.typeVars, required this.closures})
+ : super(closures, typeVars);
+
+ @override
+ Set<String> typeVarsFreeInClosureArguments(Closure closure) =>
+ closure.argTypes.toSet();
+
+ @override
+ Set<String> typeVarsFreeInClosureReturns(Closure closure) =>
+ closure.retTypes.toSet();
+}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index be22f29..3b7e4a3 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -9,6 +9,7 @@
import 'package:analysis_server/src/collections.dart';
import 'package:analysis_server/src/domain_abstract.dart';
import 'package:analysis_server/src/handler/legacy/edit_bulk_fixes.dart';
+import 'package:analysis_server/src/handler/legacy/edit_format.dart';
import 'package:analysis_server/src/handler/legacy/edit_format_if_enabled.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_assists.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_fixes.dart';
@@ -16,11 +17,11 @@
import 'package:analysis_server/src/handler/legacy/edit_get_statement_completion.dart';
import 'package:analysis_server/src/handler/legacy/edit_import_elements.dart';
import 'package:analysis_server/src/handler/legacy/edit_is_postfix_completion_applicable.dart';
+import 'package:analysis_server/src/handler/legacy/edit_list_postfix_completion_templates.dart';
import 'package:analysis_server/src/handler/legacy/edit_organize_directives.dart';
import 'package:analysis_server/src/handler/legacy/edit_sort_members.dart';
import 'package:analysis_server/src/protocol_server.dart'
hide AnalysisError, Element;
-import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
import 'package:analysis_server/src/services/correction/status.dart';
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
@@ -28,7 +29,6 @@
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
-import 'package:dart_style/dart_style.dart';
int test_resetCount = 0;
@@ -57,69 +57,14 @@
_newRefactoringManager();
}
- Response format(Request request) {
- server.options.analytics?.sendEvent('edit', 'format');
-
- var params = EditFormatParams.fromRequest(request);
- var file = params.file;
-
- String unformattedCode;
- try {
- var resource = server.resourceProvider.getFile(file);
- unformattedCode = resource.readAsStringSync();
- } catch (e) {
- return Response.formatInvalidFile(request);
- }
-
- int? start = params.selectionOffset;
- int? length = params.selectionLength;
-
- // No need to preserve 0,0 selection
- if (start == 0 && length == 0) {
- start = null;
- length = null;
- }
-
- var code = SourceCode(unformattedCode,
- uri: null,
- isCompilationUnit: true,
- selectionStart: start,
- selectionLength: length);
- var formatter = DartFormatter(pageWidth: params.lineLength);
- SourceCode formattedResult;
- try {
- formattedResult = formatter.formatSource(code);
- } on FormatterException {
- return Response.formatWithErrors(request);
- }
- var formattedSource = formattedResult.text;
-
- var edits = <SourceEdit>[];
-
- if (formattedSource != unformattedCode) {
- //TODO: replace full replacements with smaller, more targeted edits
- var edit = SourceEdit(0, unformattedCode.length, formattedSource);
- edits.add(edit);
- }
-
- var newStart = formattedResult.selectionStart;
- var newLength = formattedResult.selectionLength;
-
- // Sending null start/length values would violate protocol, so convert back
- // to 0.
- newStart ??= 0;
- newLength ??= 0;
-
- return EditFormatResult(edits, newStart, newLength).toResponse(request.id);
- }
-
@override
Response? handleRequest(
Request request, CancellationToken cancellationToken) {
try {
var requestName = request.method;
if (requestName == EDIT_REQUEST_FORMAT) {
- return format(request);
+ EditFormatHandler(server, request, cancellationToken).handle();
+ return Response.DELAYED_RESPONSE;
} else if (requestName == EDIT_REQUEST_FORMAT_IF_ENABLED) {
EditFormatIfEnabledHandler(server, request, cancellationToken).handle();
return Response.DELAYED_RESPONSE;
@@ -162,7 +107,10 @@
return Response.DELAYED_RESPONSE;
} else if (requestName ==
EDIT_REQUEST_LIST_POSTFIX_COMPLETION_TEMPLATES) {
- return listPostfixCompletionTemplates(request);
+ EditListPostfixCompletionTemplatesHandler(
+ server, request, cancellationToken)
+ .handle();
+ return Response.DELAYED_RESPONSE;
}
} on RequestFailure catch (exception) {
return exception.response;
@@ -170,16 +118,6 @@
return null;
}
- Response listPostfixCompletionTemplates(Request request) {
- var templates = DartPostfixCompletion.ALL_TEMPLATES
- .map((PostfixCompletionKind kind) =>
- PostfixTemplateDescriptor(kind.name, kind.key, kind.example))
- .toList();
-
- return EditListPostfixCompletionTemplatesResult(templates)
- .toResponse(request.id);
- }
-
Future<void> _getAvailableRefactorings(Request request) async {
var params = EditGetAvailableRefactoringsParams.fromRequest(request);
var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
new file mode 100644
index 0000000..ca6d691
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:dart_style/src/dart_formatter.dart';
+import 'package:dart_style/src/exceptions.dart';
+import 'package:dart_style/src/source_code.dart';
+
+/// The handler for the `edit.format` request.
+class EditFormatHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ EditFormatHandler(AnalysisServer server, Request request,
+ CancellationToken cancellationToken)
+ : super(server, request, cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ server.options.analytics?.sendEvent('edit', 'format');
+
+ var params = EditFormatParams.fromRequest(request);
+ var file = params.file;
+//
+ String unformattedCode;
+ try {
+ var resource = server.resourceProvider.getFile(file);
+ unformattedCode = resource.readAsStringSync();
+ } catch (e) {
+ sendResponse(Response.formatInvalidFile(request));
+ return;
+ }
+
+ int? start = params.selectionOffset;
+ int? length = params.selectionLength;
+
+ // No need to preserve 0,0 selection
+ if (start == 0 && length == 0) {
+ start = null;
+ length = null;
+ }
+
+ var code = SourceCode(unformattedCode,
+ uri: null,
+ isCompilationUnit: true,
+ selectionStart: start,
+ selectionLength: length);
+ var formatter = DartFormatter(pageWidth: params.lineLength);
+ SourceCode formattedResult;
+ try {
+ formattedResult = formatter.formatSource(code);
+ } on FormatterException {
+ sendResponse(Response.formatWithErrors(request));
+ return;
+ }
+ var formattedSource = formattedResult.text;
+
+ var edits = <SourceEdit>[];
+
+ if (formattedSource != unformattedCode) {
+ //TODO: replace full replacements with smaller, more targeted edits
+ var edit = SourceEdit(0, unformattedCode.length, formattedSource);
+ edits.add(edit);
+ }
+
+ var newStart = formattedResult.selectionStart;
+ var newLength = formattedResult.selectionLength;
+
+ // Sending null start/length values would violate protocol, so convert back
+ // to 0.
+ newStart ??= 0;
+ newLength ??= 0;
+
+ sendResult(EditFormatResult(edits, newStart, newLength));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart
new file mode 100644
index 0000000..b990eea
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `edit.listPostfixCompletionTemplates` request.
+class EditListPostfixCompletionTemplatesHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ EditListPostfixCompletionTemplatesHandler(AnalysisServer server,
+ Request request, CancellationToken cancellationToken)
+ : super(server, request, cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var templates = DartPostfixCompletion.ALL_TEMPLATES
+ .map((PostfixCompletionKind kind) =>
+ PostfixTemplateDescriptor(kind.name, kind.key, kind.example))
+ .toList();
+ sendResult(EditListPostfixCompletionTemplatesResult(templates));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
index 94e26f2..e125e84 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
@@ -4,18 +4,19 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
class CreateConstructorForFinalFields extends CorrectionProducer {
@override
FixKind get fixKind => DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS;
- bool get _isNonNullable => unit.featureSet.isEnabled(Feature.non_nullable);
-
@override
Future<void> compute(ChangeBuilder builder) async {
if (node is! SimpleIdentifier || node.parent is! VariableDeclaration) {
@@ -33,15 +34,12 @@
return;
}
- // prepare names of uninitialized final fields
- var fieldNames = <String>[];
+ var variableLists = <VariableDeclarationList>[];
for (var member in classDeclaration.members) {
if (member is FieldDeclaration) {
var variableList = member.fields;
if (variableList.isFinal && !variableList.isLate) {
- fieldNames.addAll(variableList.variables
- .where((v) => v.initializer == null)
- .map((v) => v.name.name));
+ variableLists.add(variableList);
}
}
}
@@ -59,41 +57,22 @@
if (keyClass == null) {
return;
}
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(targetLocation.offset, (builder) {
- builder.write(targetLocation.prefix);
- builder.write('const ');
- builder.write(className);
- builder.write('({');
- builder.writeType(
- keyClass.instantiate(
- typeArguments: const [],
- nullabilitySuffix: _isNonNullable
- ? NullabilitySuffix.question
- : NullabilitySuffix.star,
- ),
- );
- builder.write(' key');
- var childrenFields = <String>[];
- for (var fieldName in fieldNames) {
- if (fieldName == 'child' || fieldName == 'children') {
- childrenFields.add(fieldName);
- continue;
- }
- builder.write(', this.');
- builder.write(fieldName);
- }
- for (var fieldName in childrenFields) {
- builder.write(', this.');
- builder.write(fieldName);
- }
-
- builder.write('}) : super(key: key);');
- builder.write(targetLocation.suffix);
- });
- });
+ if (unit.featureSet.isEnabled(Feature.super_parameters)) {
+ await _withSuperParameters(
+ builder, targetLocation, className, variableLists);
+ } else {
+ await _withoutSuperParameters(
+ builder, targetLocation, className, keyClass, variableLists);
+ }
} else {
+ var fieldNames = <String>[];
+ for (var variableList in variableLists) {
+ fieldNames.addAll(variableList.variables
+ .where((v) => v.initializer == null)
+ .map((v) => v.name.name));
+ }
+
await builder.addDartFileEdit(file, (builder) {
builder.addInsertion(targetLocation.offset, (builder) {
builder.write(targetLocation.prefix);
@@ -105,6 +84,96 @@
}
}
+ Future<void> _withoutSuperParameters(
+ ChangeBuilder builder,
+ ClassMemberLocation targetLocation,
+ String className,
+ ClassElement keyClass,
+ List<VariableDeclarationList> variableLists) async {
+ var isNonNullable = unit.featureSet.isEnabled(Feature.non_nullable);
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(targetLocation.offset, (builder) {
+ builder.write(targetLocation.prefix);
+ builder.write('const ');
+ builder.write(className);
+ builder.write('({');
+ builder.writeType(
+ keyClass.instantiate(
+ typeArguments: const [],
+ nullabilitySuffix: isNonNullable
+ ? NullabilitySuffix.question
+ : NullabilitySuffix.star,
+ ),
+ );
+ builder.write(' key');
+
+ _writeParameters(builder, variableLists, isNonNullable);
+
+ builder.write('}) : super(key: key);');
+ builder.write(targetLocation.suffix);
+ });
+ });
+ }
+
+ Future<void> _withSuperParameters(
+ ChangeBuilder builder,
+ ClassMemberLocation targetLocation,
+ String className,
+ List<VariableDeclarationList> variableLists) async {
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(targetLocation.offset, (builder) {
+ builder.write(targetLocation.prefix);
+ builder.write('const ');
+ builder.write(className);
+ builder.write('({');
+ builder.write('super.key');
+
+ _writeParameters(builder, variableLists, true);
+
+ builder.write('});');
+ builder.write(targetLocation.suffix);
+ });
+ });
+ }
+
+ void _writeParameters(DartEditBuilder builder,
+ List<VariableDeclarationList> variableLists, bool isNonNullable) {
+ var childrenFields = <String>[];
+ var childrenNullables = <bool>[];
+ for (var variableList in variableLists) {
+ var fieldNames = variableList.variables
+ .where((v) => v.initializer == null)
+ .map((v) => v.name.name);
+
+ for (var fieldName in fieldNames) {
+ if (fieldName == 'child' || fieldName == 'children') {
+ childrenFields.add(fieldName);
+ childrenNullables.add(variableList.type?.type?.nullabilitySuffix ==
+ NullabilitySuffix.question);
+ continue;
+ }
+ builder.write(', ');
+ if (isNonNullable &&
+ variableList.type?.type?.nullabilitySuffix !=
+ NullabilitySuffix.question) {
+ builder.write('required ');
+ }
+ builder.write('this.');
+ builder.write(fieldName);
+ }
+ }
+ for (var i = 0; i < childrenFields.length; i++) {
+ var fieldName = childrenFields[i];
+ var nullableField = childrenNullables[i];
+ builder.write(', ');
+ if (isNonNullable && !nullableField) {
+ builder.write('required ');
+ }
+ builder.write('this.');
+ builder.write(fieldName);
+ }
+ }
+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
static CreateConstructorForFinalFields newInstance() =>
CreateConstructorForFinalFields();
diff --git a/pkg/analysis_server/test/analysis/get_errors_test.dart b/pkg/analysis_server/test/analysis/get_errors_test.dart
index 1ce1f0a..e3f0c6e 100644
--- a/pkg/analysis_server/test/analysis/get_errors_test.dart
+++ b/pkg/analysis_server/test/analysis/get_errors_test.dart
@@ -33,7 +33,7 @@
}
''');
- await server.onAnalysisComplete;
+ await waitForTasksFinished();
var errors = await _getErrors(testFile.path);
expect(errors, hasLength(1));
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index 9120539..bb65e9a 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -48,7 +48,7 @@
}
Future<HoverInformation?> prepareHoverAt(int offset) async {
- await server.onAnalysisComplete;
+ await waitForTasksFinished();
var request = AnalysisGetHoverParams(testFile.path, offset).toRequest('0');
var response = await handleSuccessfulRequest(request);
var result = AnalysisGetHoverResult.fromResponse(response);
diff --git a/pkg/analysis_server/test/analysis/notification_analysis_options_test.dart b/pkg/analysis_server/test/analysis/notification_analysis_options_test.dart
index b82f567..f783d03 100644
--- a/pkg/analysis_server/test/analysis/notification_analysis_options_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_analysis_options_test.dart
@@ -6,13 +6,13 @@
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart'
hide AnalysisOptions;
-import 'package:analysis_server/src/domain_analysis.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:linter/src/rules.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -21,8 +21,9 @@
}
@reflectiveTest
-class AnalysisOptionsFileNotificationTest extends AbstractAnalysisTest {
- Map<String, List<AnalysisError>> filesErrors = {};
+class AnalysisOptionsFileNotificationTest extends PubPackageAnalysisServerTest {
+ late File optionsFile;
+ Map<File, List<AnalysisError>> filesErrors = {};
final testSource = '''
main() {
@@ -33,40 +34,27 @@
List<AnalysisError> get errors => filesErrors[testFile]!;
- List<AnalysisError> get optionsFileErrors => filesErrors[optionsFilePath]!;
-
- String get optionsFilePath => '$projectPath/analysis_options.yaml';
+ List<AnalysisError> get optionsFileErrors => filesErrors[optionsFile]!;
List<AnalysisError> get testFileErrors => filesErrors[testFile]!;
void addOptionsFile(String contents) {
- newFile2(optionsFilePath, contents);
+ optionsFile = newAnalysisOptionsYamlFile2(testPackageRootPath, contents);
}
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
- filesErrors[decoded.file] = decoded.errors;
+ filesErrors[getFile(decoded.file)] = decoded.errors;
}
}
- Future<void> setAnalysisRoot() async {
- await setRoots(included: [projectPath], excluded: []);
- }
-
@override
- void setUp() {
+ Future<void> setUp() async {
registerLintRules();
super.setUp();
- server.handlers = [AnalysisDomainHandler(server)];
- }
-
- @override
- void tearDown() {
- filesErrors[optionsFilePath] = [];
- filesErrors[testFile] = [];
- super.tearDown();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_error_filter() async {
@@ -82,8 +70,6 @@
}
''');
- await setAnalysisRoot();
-
await waitForTasksFinished();
// Verify options file.
@@ -109,8 +95,6 @@
}
''');
- await setAnalysisRoot();
-
await waitForTasksFinished();
// Verify options file.
@@ -148,7 +132,6 @@
''');
addTestFile(testSource);
- await setAnalysisRoot();
await waitForTasksFinished();
@@ -174,7 +157,6 @@
''');
addTestFile(testSource);
- await setAnalysisRoot();
await waitForTasksFinished();
@@ -186,15 +168,11 @@
Future<void> test_options_file_added() async {
addTestFile(testSource);
- await setAnalysisRoot();
await waitForTasksFinished();
// Verify that lints are disabled.
- expect(analysisOptions.lint, false);
-
- // Clear errors.
- filesErrors[testFile] = [];
+ expect(testFileAnalysisOptions.lint, false);
// Add options file with a lint enabled.
addOptionsFile('''
@@ -213,7 +191,6 @@
addOptionsFile('''
; #bang
''');
- await setAnalysisRoot();
await waitForTasksFinished();
@@ -231,25 +208,21 @@
''');
addTestFile(testSource);
- await setAnalysisRoot();
await waitForTasksFinished();
verifyLintsEnabled(['camel_case_types']);
- // Clear errors.
- filesErrors[testFile] = [];
-
- deleteFile(optionsFilePath);
+ deleteFile(optionsFile.path);
await pumpEventQueue();
await waitForTasksFinished();
- expect(analysisOptions.lint, false);
+ expect(testFileAnalysisOptions.lint, false);
}
void verifyLintsEnabled(List<String> lints) {
- var options = analysisOptions;
+ var options = testFileAnalysisOptions;
expect(options.lint, true);
var rules = options.lintRules.map((rule) => rule.name);
expect(rules, unorderedEquals(lints));
diff --git a/pkg/analysis_server/test/analysis/notification_analyzed_files_test.dart b/pkg/analysis_server/test/analysis/notification_analyzed_files_test.dart
index e2fce2f..31ae228 100644
--- a/pkg/analysis_server/test/analysis/notification_analyzed_files_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_analyzed_files_test.dart
@@ -5,10 +5,11 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -17,13 +18,14 @@
}
@reflectiveTest
-class AnalysisNotificationAnalyzedFilesTest extends AbstractAnalysisTest {
+class AnalysisNotificationAnalyzedFilesTest
+ extends PubPackageAnalysisServerTest {
late List<String> analyzedFiles;
bool analyzedFilesReceived = false;
- void assertHasFile(String filePath) {
+ void assertHasFile(File file) {
expect(analyzedFilesReceived, isTrue);
- expect(analyzedFiles, contains(filePath));
+ expect(analyzedFiles, contains(file.path));
}
void assertHasNoFile(String filePath) {
@@ -32,7 +34,9 @@
}
Future<void> prepareAnalyzedFiles() async {
- addGeneralAnalysisSubscription(GeneralAnalysisService.ANALYZED_FILES);
+ await addGeneralAnalysisSubscription(
+ GeneralAnalysisService.ANALYZED_FILES,
+ );
await pumpEventQueue(times: 5000);
}
@@ -48,7 +52,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_afterAnalysis() async {
@@ -69,8 +73,7 @@
}
Future<void> test_beforeAnalysis_excludeYamlFiles() async {
- var yamlFile = getFolder(projectPath).getChildAssumingFile('sample.yaml');
- yamlFile.writeAsStringSync('');
+ var yamlFile = newFile2('$testPackageRootPath/sample.yaml', '');
addTestFile('''
class A {}
''');
@@ -111,17 +114,19 @@
// Making a change that *does* affect the set of reachable files should
// trigger the notification to be re-sent.
addTestFile('class A {}');
- newFile2('/foo.dart', 'library foo;');
+ var foo = newFile2('/foo.dart', 'library foo;');
await prepareAnalyzedFiles();
expect(analyzedFilesReceived, isTrue);
analyzedFilesReceived = false;
modifyTestFile('import "${toUriStr('/foo.dart')}";');
await prepareAnalyzedFiles();
- assertHasFile(convertPath('/foo.dart'));
+ assertHasFile(foo);
}
void unsubscribeAnalyzedFiles() {
- removeGeneralAnalysisSubscription(GeneralAnalysisService.ANALYZED_FILES);
+ removeGeneralAnalysisSubscription(
+ GeneralAnalysisService.ANALYZED_FILES,
+ );
}
}
diff --git a/pkg/analysis_server/test/analysis/notification_closing_labels_test.dart b/pkg/analysis_server/test/analysis/notification_closing_labels_test.dart
index 42537b1..df55415 100644
--- a/pkg/analysis_server/test/analysis/notification_closing_labels_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_closing_labels_test.dart
@@ -10,7 +10,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -19,7 +19,8 @@
}
@reflectiveTest
-class AnalysisNotificationClosingLabelsTest extends AbstractAnalysisTest {
+class AnalysisNotificationClosingLabelsTest
+ extends PubPackageAnalysisServerTest {
static const sampleCode = '''
Widget build(BuildContext context) {
return /*1*/new Row(
@@ -44,7 +45,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_CLOSING_LABELS) {
var params = AnalysisClosingLabelsParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
lastLabels = params.labels;
_labelsReceived.complete(null);
}
@@ -57,7 +58,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> subscribeForLabels() async {
diff --git a/pkg/analysis_server/test/analysis/notification_errors_test.dart b/pkg/analysis_server/test/analysis/notification_errors_test.dart
index ab0e04d..ba24eaa 100644
--- a/pkg/analysis_server/test/analysis/notification_errors_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_errors_test.dart
@@ -5,16 +5,16 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
-import 'package:analysis_server/src/domain_analysis.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:linter/src/rules.dart';
+import 'package:path/path.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
import '../src/utilities/mock_packages.dart';
void main() {
@@ -24,19 +24,19 @@
}
@reflectiveTest
-class NotificationErrorsTest extends AbstractAnalysisTest {
+class NotificationErrorsTest extends PubPackageAnalysisServerTest {
late Folder pedanticFolder;
- Map<String, List<AnalysisError>?> filesErrors = {};
+ Map<File, List<AnalysisError>?> filesErrors = {};
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
- filesErrors[decoded.file] = decoded.errors;
+ filesErrors[getFile(decoded.file)] = decoded.errors;
} else if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
var decoded = AnalysisFlushResultsParams.fromNotification(notification);
for (var file in decoded.files) {
- filesErrors[file] = null;
+ filesErrors[getFile(file)] = null;
}
}
}
@@ -46,55 +46,50 @@
registerLintRules();
super.setUp();
server.pendingFilesRemoveOverlayDelay = const Duration(milliseconds: 10);
- server.handlers = [
- AnalysisDomainHandler(server),
- ];
pedanticFolder = MockPackages.instance.addPedantic(resourceProvider);
}
Future<void> test_analysisOptionsFile() async {
- var filePath = join(projectPath, 'analysis_options.yaml');
- var analysisOptionsFile = newFile2(filePath, '''
+ var analysisOptions = newAnalysisOptionsYamlFile2(testPackageRootPath, '''
linter:
rules:
- invalid_lint_rule_name
-''').path;
+''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
// Verify the error result.
//
- var errors = filesErrors[analysisOptionsFile]!;
+ var errors = filesErrors[analysisOptions]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, filePath);
+ expect(error.location.file, analysisOptions.path);
expect(error.severity, AnalysisErrorSeverity.WARNING);
expect(error.type, AnalysisErrorType.STATIC_WARNING);
}
Future<void> test_analysisOptionsFile_packageInclude() async {
- var filePath = join(projectPath, 'analysis_options.yaml');
- var analysisOptionsFile = newFile2(filePath, '''
+ var analysisOptions = newAnalysisOptionsYamlFile2(testPackageRootPath, '''
include: package:pedantic/analysis_options.yaml
-''').path;
+''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
// Verify there's an error for the import.
- var errors = filesErrors[analysisOptionsFile]!;
+ var errors = filesErrors[analysisOptions]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, filePath);
+ expect(error.location.file, analysisOptions.path);
expect(error.severity, AnalysisErrorSeverity.WARNING);
expect(error.type, AnalysisErrorType.STATIC_WARNING);
// Write a package file that allows resolving the include.
newPackageConfigJsonFile(
- projectPath,
+ testPackageRootPath,
(PackageConfigFileBuilder()
..add(name: 'pedantic', rootPath: pedanticFolder.parent.path))
.toContent(toUriStr: toUriStr),
@@ -103,26 +98,27 @@
// Ensure the errors disappear.
await waitForTasksFinished();
await pumpEventQueue();
- errors = filesErrors[analysisOptionsFile]!;
+ errors = filesErrors[analysisOptions]!;
expect(errors, hasLength(0));
}
Future<void> test_androidManifestFile() async {
- var filePath = join(projectPath, 'android', 'AndroidManifest.xml');
- var manifestFile = newFile2(filePath, '''
+ var manifestPath =
+ join(testPackageRootPath, 'android', 'AndroidManifest.xml');
+ var manifestFile = newFile2(manifestPath, '''
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.home_screen" />
</manifest>
-''').path;
- newAnalysisOptionsYamlFile2(projectPath, '''
+''');
+ newAnalysisOptionsYamlFile2(testPackageRootPath, '''
analyzer:
optional-checks:
chrome-os-manifest-checks: true
''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
@@ -131,27 +127,28 @@
var errors = filesErrors[manifestFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, filePath);
+ expect(error.location.file, manifestFile.path);
expect(error.severity, AnalysisErrorSeverity.WARNING);
expect(error.type, AnalysisErrorType.STATIC_WARNING);
}
Future<void> test_androidManifestFile_dotDirectoryIgnored() async {
- var filePath = join(projectPath, 'ios', '.symlinks', 'AndroidManifest.xml');
- var manifestFile = newFile2(filePath, '''
+ var manifestPath =
+ join(testPackageRootPath, 'ios', '.symlinks', 'AndroidManifest.xml');
+ var manifestFile = newFile2(manifestPath, '''
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.home_screen" />
</manifest>
''').path;
- newAnalysisOptionsYamlFile2(projectPath, '''
+ newAnalysisOptionsYamlFile2(testPackageRootPath, '''
analyzer:
optional-checks:
chrome-os-manifest-checks: true
''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
@@ -165,8 +162,9 @@
// Although errors are not generated for dotfolders, their contents should
// still be analyzed so that code that references them (for example
// flutter_gen) should still be updated.
- final configPath = join(projectPath, '.dart_tool/package_config.json');
- final generatedProject = join(projectPath, '.dart_tool/foo');
+ final configPath =
+ join(testPackageRootPath, '.dart_tool/package_config.json');
+ final generatedProject = join(testPackageRootPath, '.dart_tool/foo');
final generatedFile = join(generatedProject, 'lib', 'foo.dart');
// Add the generated project into package_config.json.
@@ -181,7 +179,7 @@
A? a;
''');
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
expect(filesErrors[testFile], isEmpty);
@@ -196,13 +194,12 @@
}
Future<void> test_dataFile() async {
- var filePath = join(projectPath, 'lib', 'fix_data.yaml');
- var dataFile = newFile2(filePath, '''
+ var dataFile = newFile2('$testPackageLibPath/fix_data.yaml', '''
version: 1
transforms:
-''').path;
+''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
@@ -211,7 +208,7 @@
var errors = filesErrors[dataFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, filePath);
+ expect(error.location.file, dataFile.path);
expect(error.severity, AnalysisErrorSeverity.ERROR);
expect(error.type, AnalysisErrorType.COMPILE_TIME_ERROR);
}
@@ -220,10 +217,10 @@
// Files inside dotFolders should not generate error notifications even
// if they are added to priority (priority affects only priority, not what
// is analyzed).
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('');
var brokenFile =
- newFile2(join(projectPath, '.dart_tool/broken.dart'), 'err').path;
+ newFile2(join(testPackageRootPath, '.dart_tool/broken.dart'), 'err');
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
@@ -244,17 +241,19 @@
// them to be opened (such as hovers) should not result in error notifications
// because there is no event that would flush them and they'd remain in the
// editor forever.
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('');
var brokenFile =
- newFile2(join(projectPath, '.dart_tool/broken.dart'), 'err').path;
+ newFile2('$testPackageRootPath/.dart_tool/broken.dart', 'err');
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
expect(filesErrors[brokenFile], isNull);
// Send a getHover request for the file that will cause it to be read from disk.
- await waitResponse(AnalysisGetHoverParams(brokenFile, 0).toRequest('0'));
+ await handleSuccessfulRequest(
+ AnalysisGetHoverParams(brokenFile.path, 0).toRequest('0'),
+ );
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
@@ -263,14 +262,14 @@
}
Future<void> test_excludedFolder() async {
- newAnalysisOptionsYamlFile2(projectPath, '''
+ newAnalysisOptionsYamlFile2(testPackageRootPath, '''
analyzer:
exclude:
- excluded/**
''');
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
var excludedFile =
- newFile2(join(projectPath, 'excluded/broken.dart'), 'err').path;
+ newFile2('$testPackageRootPath/excluded/broken.dart', 'err');
// There should be no errors initially.
await waitForTasksFinished();
@@ -278,21 +277,24 @@
expect(filesErrors[excludedFile], isNull);
// Triggering the file to be processed should still generate no errors.
- await waitResponse(AnalysisGetHoverParams(excludedFile, 0).toRequest('0'));
+ await handleSuccessfulRequest(
+ AnalysisGetHoverParams(excludedFile.path, 0).toRequest('0'),
+ );
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
expect(filesErrors[excludedFile], isNull);
// Opening the file should still generate no errors.
- await waitResponse(
- AnalysisSetPriorityFilesParams([excludedFile]).toRequest('0'));
+ await handleSuccessfulRequest(
+ AnalysisSetPriorityFilesParams([excludedFile.path]).toRequest('0'),
+ );
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
expect(filesErrors[excludedFile], isNull);
}
Future<void> test_importError() async {
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('''
import 'does_not_exist.dart';
@@ -312,7 +314,7 @@
Future<void> test_lintError() async {
var camelCaseTypesLintName = 'camel_case_types';
- newAnalysisOptionsYamlFile2(projectPath, '''
+ newAnalysisOptionsYamlFile2(testPackageRootPath, '''
linter:
rules:
- $camelCaseTypesLintName
@@ -320,11 +322,10 @@
addTestFile('class a { }');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
- var testDriver = server.getAnalysisDriver(testFile)!;
- var lints = testDriver.analysisOptions.lintRules;
+ var lints = testFileAnalysisOptions.lintRules;
// Registry should only contain single lint rule.
expect(lints, hasLength(1));
@@ -335,15 +336,15 @@
var errors = filesErrors[testFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, join(projectPath, 'bin', 'test.dart'));
+ expect(error.location.file, testFile.path);
expect(error.severity, AnalysisErrorSeverity.INFO);
expect(error.type, AnalysisErrorType.LINT);
expect(error.message, lint.description);
}
Future<void> test_notInAnalysisRoot() async {
- await createProject();
- var otherFile = newFile2('/other.dart', 'UnknownType V;').path;
+ await setRoots(included: [workspaceRootPath], excluded: []);
+ var otherFile = newFile2('/other.dart', 'UnknownType V;');
addTestFile('''
import '/other.dart';
main() {
@@ -357,10 +358,10 @@
Future<void> test_overlay_dotFolder() async {
// Files inside dotFolders should not generate error notifications even
// if they have overlays added.
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('');
var brokenFile =
- newFile2(join(projectPath, '.dart_tool/broken.dart'), 'err').path;
+ newFile2('$testPackageRootPath/.dart_tool/broken.dart', 'err');
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
@@ -368,9 +369,9 @@
// Add and overlay and give chance for the file to be analyzed (if
// it would).
- await waitResponse(
+ await handleSuccessfulRequest(
AnalysisUpdateContentParams({
- brokenFile: AddContentOverlay('err'),
+ brokenFile.path: AddContentOverlay('err'),
}).toRequest('1'),
);
await waitForTasksFinished();
@@ -384,14 +385,14 @@
// Overlays added for files that don't exist on disk should still generate
// error notifications. Removing the overlay if the file is not on disk
// should clear the errors.
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('');
- var brokenFile = convertPath(join(projectPath, 'broken.dart'));
+ var brokenFile = getFile('$testPackageRootPath/broken.dart');
// Add and overlay and give chance for the file to be analyzed.
- await waitResponse(
+ await handleSuccessfulRequest(
AnalysisUpdateContentParams({
- brokenFile: AddContentOverlay('err'),
+ brokenFile.path: AddContentOverlay('err'),
}).toRequest('0'),
);
await waitForTasksFinished();
@@ -401,9 +402,9 @@
expect(filesErrors[brokenFile], hasLength(greaterThan(0)));
// Remove the overlay (this file no longer exists anywhere).
- await waitResponse(
+ await handleSuccessfulRequest(
AnalysisUpdateContentParams({
- brokenFile: RemoveContentOverlay(),
+ brokenFile.path: RemoveContentOverlay(),
}).toRequest('1'),
);
@@ -424,14 +425,14 @@
// error notifications. If the file is subsequently saved to disk before the
// overlay is removed, the errors should not be flushed when the overlay is
// removed.
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('');
- var brokenFile = convertPath(join(projectPath, 'broken.dart'));
+ var brokenFile = getFile('$testPackageRootPath/broken.dart');
// Add and overlay and give chance for the file to be analyzed.
- await waitResponse(
+ await handleSuccessfulRequest(
AnalysisUpdateContentParams({
- brokenFile: AddContentOverlay('err'),
+ brokenFile.path: AddContentOverlay('err'),
}).toRequest('0'),
);
await waitForTasksFinished();
@@ -441,14 +442,14 @@
expect(filesErrors[brokenFile], hasLength(greaterThan(0)));
// Write the file to disk.
- newFile2(brokenFile, 'err');
+ brokenFile.writeAsStringSync('err');
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
// Remove the overlay.
- await waitResponse(
+ await handleSuccessfulRequest(
AnalysisUpdateContentParams({
- brokenFile: RemoveContentOverlay(),
+ brokenFile.path: RemoveContentOverlay(),
}).toRequest('1'),
);
await waitForTasksFinished();
@@ -460,14 +461,14 @@
}
Future<void> test_ParserError() async {
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('library lib');
await waitForTasksFinished();
await pumpEventQueue(times: 5000);
var errors = filesErrors[testFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, join(projectPath, 'bin', 'test.dart'));
+ expect(error.location.file, testFile.path);
expect(error.location.offset, isPositive);
expect(error.location.length, isNonNegative);
expect(error.severity, AnalysisErrorSeverity.ERROR);
@@ -476,11 +477,11 @@
}
Future<void> test_pubspecFile() async {
- var pubspecFile = newPubspecYamlFile(projectPath, '''
+ var pubspecFile = newPubspecYamlFile(testPackageRootPath, '''
version: 1.3.2
-''').path;
+''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
@@ -489,13 +490,13 @@
var errors = filesErrors[pubspecFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, pubspecFile);
+ expect(error.location.file, pubspecFile.path);
expect(error.severity, AnalysisErrorSeverity.WARNING);
expect(error.type, AnalysisErrorType.STATIC_WARNING);
//
// Fix the error and verify the new results.
//
- modifyFile(pubspecFile, '''
+ pubspecFile.writeAsStringSync('''
name: sample
version: 1.3.2
''');
@@ -507,21 +508,21 @@
}
Future<void> test_pubspecFile_lint() async {
- newAnalysisOptionsYamlFile2(projectPath, '''
+ newAnalysisOptionsYamlFile2(testPackageRootPath, '''
linter:
rules:
- sort_pub_dependencies
''');
- var pubspecFile = newPubspecYamlFile(projectPath, '''
+ var pubspecFile = newPubspecYamlFile(testPackageRootPath, '''
name: sample
dependencies:
b: any
a: any
-''').path;
+''');
- await setRoots(included: [projectPath], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
await pumpEventQueue();
//
@@ -530,13 +531,13 @@
var errors = filesErrors[pubspecFile]!;
expect(errors, hasLength(1));
var error = errors[0];
- expect(error.location.file, pubspecFile);
+ expect(error.location.file, pubspecFile.path);
expect(error.severity, AnalysisErrorSeverity.INFO);
expect(error.type, AnalysisErrorType.LINT);
//
// Fix the error and verify the new results.
//
- modifyFile(pubspecFile, '''
+ pubspecFile.writeAsStringSync('''
name: sample
dependencies:
@@ -551,7 +552,7 @@
}
Future<void> test_StaticWarning() async {
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
addTestFile('''
enum E {e1, e2}
diff --git a/pkg/analysis_server/test/analysis/notification_folding_test.dart b/pkg/analysis_server/test/analysis/notification_folding_test.dart
index add62cf..7cbda58 100644
--- a/pkg/analysis_server/test/analysis/notification_folding_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_folding_test.dart
@@ -9,7 +9,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -18,7 +18,7 @@
}
@reflectiveTest
-class AnalysisNotificationFoldingTest extends AbstractAnalysisTest {
+class AnalysisNotificationFoldingTest extends PubPackageAnalysisServerTest {
static const sampleCode = '''
import 'dart:async';
import 'dart:core';
@@ -40,7 +40,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_FOLDING) {
var params = AnalysisFoldingParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
lastRegions = params.regions;
_regionsReceived.complete(null);
}
@@ -53,7 +53,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> subscribeForFolding() async {
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index 6b6ebd6..2e70411 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -11,7 +11,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -1593,7 +1593,7 @@
}
}
-class HighlightsTestSupport extends AbstractAnalysisTest {
+class HighlightsTestSupport extends PubPackageAnalysisServerTest {
late List<HighlightRegion> regions;
final Completer<void> _resultsAvailable = Completer();
@@ -1676,7 +1676,7 @@
}
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = AnalysisHighlightsParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
regions = params.regions;
_resultsAvailable.complete();
}
@@ -1686,11 +1686,11 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
void _addLibraryForTestPart() {
- newFile2(join(testFolder, 'my_lib.dart'), '''
+ newFile2('$testPackageLibPath/my_lib.dart', '''
library lib;
part 'test.dart';
''');
diff --git a/pkg/analysis_server/test/analysis/notification_implemented_test.dart b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
index 0544f28..1b978de 100644
--- a/pkg/analysis_server/test/analysis/notification_implemented_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
@@ -5,11 +5,11 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -18,7 +18,7 @@
}
@reflectiveTest
-class AnalysisNotificationImplementedTest extends AbstractAnalysisTest {
+class AnalysisNotificationImplementedTest extends PubPackageAnalysisServerTest {
List<ImplementedClass>? implementedClasses;
List<ImplementedMember>? implementedMembers;
@@ -94,7 +94,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_IMPLEMENTED) {
var params = AnalysisImplementedParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
implementedClasses = params.classes;
implementedMembers = params.members;
}
@@ -104,7 +104,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> subscribeForImplemented() async {
@@ -122,25 +122,6 @@
assertHasImplementedClass('A {');
}
- Future<void> test_afterIncrementalResolution() async {
- await subscribeForImplemented();
- addTestFile('''
-class A {}
-class B extends A {}
-''');
- await prepareImplementedElements();
- assertHasImplementedClass('A {');
- // add a space
- implementedClasses = null;
- testCode = '''
-class A {}
-class B extends A {}
-''';
- server.updateContent('1', {testFile: AddContentOverlay(testCode)});
- await waitForImplementedElements();
- assertHasImplementedClass('A {');
- }
-
Future<void> test_class_extended() async {
addTestFile('''
class A {}
@@ -365,7 +346,7 @@
}
Future<void> test_method_withMethod_private_differentLib() async {
- newFile2(join(testFolder, 'lib.dart'), r'''
+ newFile2('$testPackageLibPath/lib.dart', r'''
import 'test.dart';
class B extends A {
void _m() {}
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index e869127..b657168 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -214,7 +214,7 @@
class AAA {}
AAA aaa;
''');
- await server.onAnalysisComplete;
+ await waitForTasksFinished();
await prepareNavigation();
assertHasRegionTarget('AAA aaa;', 'AAA {}');
}
diff --git a/pkg/analysis_server/test/analysis/notification_occurrences_test.dart b/pkg/analysis_server/test/analysis/notification_occurrences_test.dart
index ca7d1e2..8880291 100644
--- a/pkg/analysis_server/test/analysis/notification_occurrences_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_occurrences_test.dart
@@ -12,6 +12,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -20,7 +21,7 @@
}
@reflectiveTest
-class AnalysisNotificationOccurrencesTest extends AbstractAnalysisTest {
+class AnalysisNotificationOccurrencesTest extends PubPackageAnalysisServerTest {
late List<Occurrences> occurrencesList;
late Occurrences testOccurrences;
@@ -80,7 +81,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_OCCURRENCES) {
var params = AnalysisOccurrencesParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
occurrencesList = params.occurrences;
_resultsAvailable.complete();
}
@@ -90,7 +91,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_afterAnalysis() async {
diff --git a/pkg/analysis_server/test/analysis/notification_outline_test.dart b/pkg/analysis_server/test/analysis/notification_outline_test.dart
index 14dc697..262673b 100644
--- a/pkg/analysis_server/test/analysis/notification_outline_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_outline_test.dart
@@ -11,7 +11,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -20,7 +20,7 @@
}
@reflectiveTest
-class AnalysisNotificationOutlineTest extends AbstractAnalysisTest {
+class AnalysisNotificationOutlineTest extends PubPackageAnalysisServerTest {
late FileKind fileKind;
String? libraryName;
Outline? outline;
@@ -37,7 +37,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_OUTLINE) {
var params = AnalysisOutlineParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
fileKind = params.kind;
libraryName = params.libraryName;
outline = params.outline;
@@ -46,7 +46,7 @@
}
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
var params = AnalysisHighlightsParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
_highlightsReceived?.complete(null);
_highlightsReceived = null;
}
@@ -56,7 +56,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_afterAnalysis() async {
diff --git a/pkg/analysis_server/test/analysis/notification_overrides_test.dart b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
index 77b9230..ac0e7c8 100644
--- a/pkg/analysis_server/test/analysis/notification_overrides_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
@@ -11,6 +11,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -19,7 +20,7 @@
}
@reflectiveTest
-class AnalysisNotificationOverridesTest extends AbstractAnalysisTest {
+class AnalysisNotificationOverridesTest extends PubPackageAnalysisServerTest {
late List<Override> overridesList;
late Override overrideObject;
@@ -113,7 +114,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_OVERRIDES) {
var params = AnalysisOverridesParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
overridesList = params.overrides;
_resultsAvailable.complete();
}
@@ -123,7 +124,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_afterAnalysis() async {
@@ -221,7 +222,7 @@
}
Future<void> test_class_BAD_privateByPrivate_inDifferentLib() async {
- newFile2(join(testFolder, 'lib.dart'), r'''
+ newFile2('$testPackageLibPath/lib.dart', r'''
class A {
void _m() {}
}
diff --git a/pkg/analysis_server/test/analysis/reanalyze_test.dart b/pkg/analysis_server/test/analysis/reanalyze_test.dart
index 84aea11..d460e3e 100644
--- a/pkg/analysis_server/test/analysis/reanalyze_test.dart
+++ b/pkg/analysis_server/test/analysis/reanalyze_test.dart
@@ -5,11 +5,12 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -18,42 +19,45 @@
}
@reflectiveTest
-class ReanalyzeTest extends AbstractAnalysisTest {
- Map<String, List<AnalysisError>> filesErrors = {};
+class ReanalyzeTest extends PubPackageAnalysisServerTest {
+ Map<File, List<AnalysisError>> filesErrors = {};
@override
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
- filesErrors[decoded.file] = decoded.errors;
+ filesErrors[getFile(decoded.file)] = decoded.errors;
}
}
Future<void> test_reanalyze() async {
- var b = convertPath('/other/b.dart');
+ var b = getFolder(testPackageLibPath).parent.getChildAssumingFile('b.dart');
- newFile2(testFile, r'''
-import '../../other/b.dart';
+ var file = newFile2('$testPackageTestPath/a.dart', r'''
+import '../b.dart';
var b = B();
''');
- await createProject();
+
+ await setRoots(included: [testPackageTestPath], excluded: []);
// b.dart does not exist, and `B` is unresolved.
await waitForTasksFinished();
- expect(filesErrors[testFile], hasLength(2));
+ expect(filesErrors[file], hasLength(2));
// Clear errors, so that we'll notice new results.
filesErrors.clear();
// Create b.dart, reanalyzing should fix the error.
- newFile2(b, 'class B {}');
+ b.writeAsStringSync('class B {}');
// Reanalyze.
- await server.reanalyze();
+ await handleSuccessfulRequest(
+ AnalysisReanalyzeParams().toRequest('0'),
+ );
await waitForTasksFinished();
// No errors.
- expect(filesErrors[testFile], isEmpty);
+ expect(filesErrors[file], isEmpty);
}
}
diff --git a/pkg/analysis_server/test/analysis/set_priority_files_test.dart b/pkg/analysis_server/test/analysis/set_priority_files_test.dart
index 16b93d0..47b2f68 100644
--- a/pkg/analysis_server/test/analysis/set_priority_files_test.dart
+++ b/pkg/analysis_server/test/analysis/set_priority_files_test.dart
@@ -4,12 +4,11 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
-import 'package:analysis_server/src/domain_analysis.dart';
-import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/file_system/file_system.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
import '../mocks.dart';
void main() {
@@ -19,18 +18,15 @@
}
@reflectiveTest
-class SetPriorityFilesTest extends AbstractAnalysisTest {
+class SetPriorityFilesTest extends PubPackageAnalysisServerTest {
@override
Future<void> setUp() async {
super.setUp();
- server.handlers = [
- AnalysisDomainHandler(server),
- ];
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_fileDoesNotExist() async {
- var file = convertPath('$projectPath/doesNotExist.dart');
+ var file = getFile('$testPackageLibPath/doesNotExist.dart');
var response = await _setPriorityFile(file);
expect(response, isResponseSuccess('0'));
}
@@ -45,90 +41,98 @@
}
Future<void> test_fileInAnalysisRootAddedLater() async {
- var path = convertPath('/other/file.dart');
- newFile2(path, '');
- await _setPriorityFile(path);
- await _setAnalysisRoots('/other');
- _verifyPriorityFiles(path);
+ var file = newFile2('/other/file.dart', '');
+ await _setPriorityFile(file);
+ await setRoots(included: [file.parent.path], excluded: []);
+ _verifyPriorityFiles(file);
}
Future<void> test_fileInSdk() async {
addTestFile('');
// set priority files
- var filePath = convertPath('/lib/convert/convert.dart');
- var response = await _setPriorityFile(filePath);
+ var file = sdkRoot
+ .getChildAssumingFolder('lib')
+ .getChildAssumingFolder('convert')
+ .getChildAssumingFile('convert.dart');
+ var response = await _setPriorityFile(file);
expect(response, isResponseSuccess('0'));
// verify
- _verifyPriorityFiles(filePath);
+ _verifyPriorityFiles(file);
}
Future<void> test_fileNotInAnalysisRoot() async {
- var path = convertPath('/other/file.dart');
- newFile2(path, '');
- await _setPriorityFile(path);
- _verifyPriorityFiles(path);
+ var file = newFile2('/other/file.dart', '');
+ await _setPriorityFile(file);
+ _verifyPriorityFiles(file);
}
Future<void> test_ignoredInAnalysisOptions() async {
- var sampleFile = convertPath('$projectPath/samples/sample.dart');
- newFile2('$projectPath/${file_paths.analysisOptionsYaml}', r'''
+ newAnalysisOptionsYamlFile2(testPackageRootPath, r'''
analyzer:
exclude:
- 'samples/**'
''');
- newFile2(sampleFile, '');
+ var file = newFile2('$testPackageRootPath/samples/sample.dart', '');
// attempt to set priority file
- await _setPriorityFile(sampleFile);
- _verifyPriorityFiles(sampleFile);
+ await _setPriorityFile(file);
+ _verifyPriorityFiles(file);
}
Future<void> test_ignoredInAnalysisOptions_inChildContext() async {
- newPackageConfigJsonFile(projectPath, '');
- newPackageConfigJsonFile('$projectPath/child', '');
- var sampleFile = convertPath('$projectPath/child/samples/sample.dart');
- newFile2('$projectPath/child/${file_paths.analysisOptionsYaml}', r'''
+ newPackageConfigJsonFile(testPackageRootPath, '');
+ newPackageConfigJsonFile('$testPackageRootPath/child', '');
+ var sampleFile = newFile2(
+ '$testPackageRootPath/child/samples/sample.dart',
+ '',
+ );
+ newAnalysisOptionsYamlFile2(testPackageRootPath, r'''
analyzer:
exclude:
- 'samples/**'
''');
- newFile2(sampleFile, '');
// attempt to set priority file
await _setPriorityFile(sampleFile);
_verifyPriorityFiles(sampleFile);
}
Future<void> test_ignoredInAnalysisOptions_inRootContext() async {
- newPackageConfigJsonFile(projectPath, '');
- newPackageConfigJsonFile('$projectPath/child', '');
- var sampleFile = convertPath('$projectPath/child/samples/sample.dart');
- newFile2('$projectPath/${file_paths.analysisOptionsYaml}', r'''
+ newPackageConfigJsonFile(testPackageRootPath, '');
+ newPackageConfigJsonFile('$testPackageRootPath/child', '');
+ var sampleFile = newFile2(
+ '$testPackageRootPath/child/samples/sample.dart',
+ '',
+ );
+ newAnalysisOptionsYamlFile2(testPackageRootPath, r'''
analyzer:
exclude:
- 'child/samples/**'
''');
- newFile2(sampleFile, '');
// attempt to set priority file
await _setPriorityFile(sampleFile);
_verifyPriorityFiles(sampleFile);
}
Future<void> test_invalidFilePathFormat_notAbsolute() async {
- var request = AnalysisSetPriorityFilesParams(['test.dart']).toRequest('0');
- var response = await waitResponse(request);
- expect(
+ var response = await handleRequest(
+ AnalysisSetPriorityFilesParams([
+ 'test.dart',
+ ]).toRequest('0'),
+ );
+ assertResponseFailure(
response,
- isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
+ requestId: '0',
+ errorCode: RequestErrorCode.INVALID_FILE_PATH_FORMAT,
);
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
- var request =
- AnalysisSetPriorityFilesParams([convertPath('/foo/../bar/test.dart')])
- .toRequest('0');
- var response = await waitResponse(request);
- expect(
+ var response = await handleRequest(AnalysisSetPriorityFilesParams([
+ convertPath('/foo/../bar/test.dart'),
+ ]).toRequest('0'));
+ assertResponseFailure(
response,
- isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
+ requestId: '0',
+ errorCode: RequestErrorCode.INVALID_FILE_PATH_FORMAT,
);
}
@@ -139,22 +143,20 @@
expect(response, isResponseSuccess('0'));
// verify
var params = pluginManager.analysisSetPriorityFilesParams!;
- expect(params.files, <String>[testFile]);
+ expect(params.files, [testFile.path]);
}
- Future<Response> _setAnalysisRoots(String folder) async {
- var request = AnalysisSetAnalysisRootsParams([folder], []).toRequest('1');
- return await serverChannel.sendRequest(request);
+ Future<Response> _setPriorityFile(File file) async {
+ return await handleSuccessfulRequest(
+ AnalysisSetPriorityFilesParams(<String>[
+ file.path,
+ ]).toRequest('0'),
+ );
}
- Future<Response> _setPriorityFile(String file) async {
- var request = AnalysisSetPriorityFilesParams(<String>[file]).toRequest('0');
- return await serverChannel.sendRequest(request);
- }
-
- void _verifyPriorityFiles(String path) {
- var driver = server.getAnalysisDriver(path)!;
+ void _verifyPriorityFiles(File file) {
+ var driver = server.getAnalysisDriver(file.path)!;
var prioritySources = driver.priorityFiles;
- expect(prioritySources, [path]);
+ expect(prioritySources, [file.path]);
}
}
diff --git a/pkg/analysis_server/test/analysis/update_content_test.dart b/pkg/analysis_server/test/analysis/update_content_test.dart
index b5213d9..853eebb 100644
--- a/pkg/analysis_server/test/analysis/update_content_test.dart
+++ b/pkg/analysis_server/test/analysis/update_content_test.dart
@@ -5,13 +5,14 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
-import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
import '../mocks.dart';
+import '../services/refactoring/abstract_rename.dart';
void main() {
defineReflectiveSuite(() {
@@ -20,8 +21,8 @@
}
@reflectiveTest
-class UpdateContentTest extends AbstractAnalysisTest {
- Map<String, List<String>> filesErrors = {};
+class UpdateContentTest extends PubPackageAnalysisServerTest {
+ Map<File, List<String>> filesErrors = {};
int serverErrorCount = 0;
int navigationCount = 0;
@@ -31,7 +32,7 @@
var decoded = AnalysisErrorsParams.fromNotification(notification);
String _format(AnalysisError e) =>
'${e.location.startLine}: ${e.message}';
- filesErrors[decoded.file] = decoded.errors.map(_format).toList();
+ filesErrors[getFile(decoded.file)] = decoded.errors.map(_format).toList();
}
if (notification.event == ANALYSIS_NOTIFICATION_NAVIGATION) {
navigationCount++;
@@ -44,25 +45,28 @@
void test_illegal_ChangeContentOverlay() async {
// It should be illegal to send a ChangeContentOverlay for a file that
// doesn't have an overlay yet.
- await createProject();
- addTestFile('library foo;');
- var id = 'myId';
- try {
- server.updateContent(id, {
- testFile: ChangeContentOverlay([SourceEdit(8, 3, 'bar')])
- });
- fail('Expected an exception to be thrown');
- } on RequestFailure catch (e) {
- expect(e.response.id, id);
- expect(e.response.error!.code, RequestErrorCode.INVALID_OVERLAY_CHANGE);
- }
+ addTestFile('');
+ await setRoots(included: [workspaceRootPath], excluded: []);
+ var response = await handleRequest(
+ AnalysisUpdateContentParams({
+ testFile.path: ChangeContentOverlay([
+ SourceEdit(0, 0, ''),
+ ]),
+ }).toRequest('0'),
+ );
+ assertResponseFailure(
+ response,
+ requestId: '0',
+ errorCode: RequestErrorCode.INVALID_OVERLAY_CHANGE,
+ );
}
Future<void> test_invalidFilePathFormat_notAbsolute() async {
- var request = AnalysisUpdateContentParams(
- {'test.dart': AddContentOverlay('')},
- ).toRequest('0');
- var response = await waitResponse(request);
+ var response = await handleRequest(
+ AnalysisUpdateContentParams({
+ 'test.dart': AddContentOverlay(''),
+ }).toRequest('0'),
+ );
expect(
response,
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
@@ -70,10 +74,11 @@
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
- var request = AnalysisUpdateContentParams(
- {convertPath('/foo/../bar/test.dart'): AddContentOverlay('')},
- ).toRequest('0');
- var response = await waitResponse(request);
+ var response = await handleRequest(
+ AnalysisUpdateContentParams({
+ convertPath('/foo/../bar/test.dart'): AddContentOverlay(''),
+ }).toRequest('0'),
+ );
expect(
response,
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
@@ -81,41 +86,54 @@
}
Future<void> test_multiple_contexts() async {
- var project1path = convertPath('/project1');
- var project2path = convertPath('/project2');
- var fooPath = newFile2('/project1/foo.dart', '''
-library foo;
-import '../project2/baz.dart';
-main() { f(); }''').path;
- var barPath = newFile2('/project2/bar.dart', '''
-library bar;
-import 'baz.dart';
-main() { f(); }''').path;
- var bazPath = newFile2('/project2/baz.dart', '''
-library baz;
-f(int i) {}
-''').path;
- await setRoots(included: [project1path, project2path], excluded: []);
+ writePackageConfig(
+ getFolder(workspaceRootPath),
+ PackageConfigFileBuilder()
+ ..add(name: 'aaa', rootPath: '$workspaceRootPath/aaa'),
+ );
+
+ var aaa = newFile2('$workspaceRootPath/aaa/lib/aaa.dart', r'''
+void f(int _) {}
+''');
+
+ var foo = newFile2('$workspaceRootPath/foo/lib/foo.dart', r'''
+import 'package:aaa/aaa.dart';
+void main() {
+ f();
+}
+''');
+
+ var bar = newFile2('$workspaceRootPath/bar/lib/bar.dart', r'''
+import 'package:aaa/aaa.dart';
+void main() {
+ f();
+}
+''');
+
+ await setRoots(included: [
+ foo.parent.path,
+ bar.parent.path,
+ ], excluded: []);
+
{
- await server.onAnalysisComplete;
+ await waitForTasksFinished();
// Files foo.dart and bar.dart should both have errors, since they both
// call f() with the wrong number of arguments.
- expect(filesErrors[fooPath], hasLength(1));
- expect(filesErrors[barPath], hasLength(1));
+ expect(filesErrors[foo], hasLength(1));
+ expect(filesErrors[bar], hasLength(1));
// Overlay the content of baz.dart to eliminate the errors.
- server.updateContent('1', {
- bazPath: AddContentOverlay('''
-library baz;
-f() {}
-''')
- });
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams(
+ {aaa.path: AddContentOverlay('void f() {}')}).toRequest('0'),
+ );
}
+
{
- await server.onAnalysisComplete;
+ await waitForTasksFinished();
// The overlay should have been propagated to both contexts, causing both
// foo.dart and bar.dart to be reanalyzed and found to be free of errors.
- expect(filesErrors[fooPath], isEmpty);
- expect(filesErrors[barPath], isEmpty);
+ expect(filesErrors[foo], isEmpty);
+ expect(filesErrors[bar], isEmpty);
}
}
@@ -125,17 +143,23 @@
var project = newFolder('/project');
await setRoots(included: [project.path], excluded: []);
- server.updateContent('1',
- {'/project/main.dart': AddContentOverlay('import "target.dart";')});
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ '/project/main.dart': AddContentOverlay('import "target.dart";'),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
expect(filesErrors, {
'/project/main.dart': ["1: Target of URI doesn't exist: 'target.dart'."],
'/project/target.dart': []
});
- server.updateContent('1',
- {'/project/target.dart': AddContentOverlay('import "none.dart";')});
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ '/project/target.dart': AddContentOverlay('import "none.dart";')
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
expect(filesErrors, {
'/project/main.dart': ['1: Unused import.'],
'/project/target.dart': ["1: Target of URI doesn't exist: 'none.dart'."],
@@ -144,51 +168,59 @@
}
Future<void> test_overlayOnly() async {
- var filePath1 = convertPath('/User/project1/test.dart');
- var filePath2 = convertPath('/User/project2/test.dart');
- var folderPath1 = newFolder('/User/project1').path;
- var folderPath2 = newFolder('/User/project2').path;
+ var a = newFile2('$testPackageLibPath/a.dart', '');
+ var b = getFile('$testPackageLibPath/b.dart');
- await setRoots(included: [folderPath1, folderPath2], excluded: []);
+ await setRoots(included: [workspaceRootPath], excluded: []);
+ await waitForTasksFinished();
+ expect(filesErrors[a], isEmpty);
+ expect(filesErrors[b], isNull);
- // exactly 2 contexts
- expect(server.driverMap, hasLength(2));
- var driver1 = server.getAnalysisDriver(filePath1)!;
- var driver2 = server.getAnalysisDriver(filePath2)!;
+ // Add `b.dart` overlay, analyzed.
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ b.path: AddContentOverlay(''),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
+ expect(filesErrors[a], isEmpty);
+ expect(filesErrors[b], isEmpty);
- // no sources
- expect(await _getUserSources(driver1), isEmpty);
- expect(await _getUserSources(driver2), isEmpty);
-
- // add an overlay - new Source in context1
- server.updateContent('1', {filePath1: AddContentOverlay('')});
- expect(await _getUserSources(driver1), [filePath1]);
- expect(await _getUserSources(driver2), isEmpty);
-
- // remove the overlay - no sources
- server.updateContent('2', {filePath1: RemoveContentOverlay()});
-
- // The file isn't removed from the list of added sources.
-// expect(_getUserSources(driver1), isEmpty);
- expect(await _getUserSources(driver2), isEmpty);
+ // Add `b.dart` overlay, analyzed.
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ b.path: RemoveContentOverlay(),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
+ expect(filesErrors[a], isEmpty);
+ // TODO(scheglov) We should get "flush" notification.
+ // expect(filesErrors[b], isNull);
}
@failingTest
Future<void> test_sendNoticesAfterNopChange() async {
// The errors are empty on the last line.
addTestFile('');
- await createProject();
- await server.onAnalysisComplete;
+ await setRoots(included: [workspaceRootPath], excluded: []);
+ await waitForTasksFinished();
// add an overlay
- server.updateContent(
- '1', {testFile: AddContentOverlay('main() {} main() {}')});
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ testFile.path: AddContentOverlay('main() {} main() {}'),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
// clear errors and make a no-op change
filesErrors.clear();
- server.updateContent('2', {
- testFile: ChangeContentOverlay([SourceEdit(0, 4, 'main')])
- });
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ testFile.path: ChangeContentOverlay([
+ SourceEdit(0, 4, 'main'),
+ ]),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
// errors should have been resent
expect(filesErrors, isNotEmpty);
}
@@ -197,31 +229,40 @@
Future<void> test_sendNoticesAfterNopChange_flushedUnit() async {
// The list of errors is empty on the last line.
addTestFile('');
- await createProject();
- await server.onAnalysisComplete;
+ await setRoots(included: [workspaceRootPath], excluded: []);
+ await waitForTasksFinished();
// add an overlay
- server.updateContent(
- '1', {testFile: AddContentOverlay('main() {} main() {}')});
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ testFile.path: AddContentOverlay('main() {} main() {}'),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
// clear errors and make a no-op change
filesErrors.clear();
- server.updateContent('2', {
- testFile: ChangeContentOverlay([SourceEdit(0, 4, 'main')])
- });
- await server.onAnalysisComplete;
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ testFile.path: ChangeContentOverlay([
+ SourceEdit(0, 4, 'main'),
+ ]),
+ }).toRequest('0'),
+ );
+ await waitForTasksFinished();
// errors should have been resent
expect(filesErrors, isNotEmpty);
}
- void test_sentToPlugins() {
- var filePath = convertPath('/project/target.dart');
+ Future<void> test_sentToPlugins() async {
+ var filePath = convertPath('$testPackageLibPath/a.dart');
var fileContent = 'import "none.dart";';
//
// Add
//
- handleSuccessfulRequest(AnalysisUpdateContentParams(
- <String, Object>{filePath: AddContentOverlay(fileContent)})
- .toRequest('0'));
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams(<String, Object>{
+ filePath: AddContentOverlay(fileContent),
+ }).toRequest('0'),
+ );
var params = pluginManager.analysisUpdateContentParams!;
var files = params.files;
expect(files, hasLength(1));
@@ -233,10 +274,14 @@
// Change
//
pluginManager.analysisUpdateContentParams = null;
- handleSuccessfulRequest(AnalysisUpdateContentParams(<String, Object>{
- filePath: ChangeContentOverlay(
- <SourceEdit>[SourceEdit(8, 1, "'"), SourceEdit(18, 1, "'")])
- }).toRequest('1'));
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams(<String, Object>{
+ filePath: ChangeContentOverlay(<SourceEdit>[
+ SourceEdit(8, 1, "'"),
+ SourceEdit(18, 1, "'"),
+ ]),
+ }).toRequest('1'),
+ );
params = pluginManager.analysisUpdateContentParams!;
expect(params, isNotNull);
files = params.files;
@@ -249,8 +294,11 @@
// Remove
//
pluginManager.analysisUpdateContentParams = null;
- handleSuccessfulRequest(AnalysisUpdateContentParams(
- <String, Object>{filePath: RemoveContentOverlay()}).toRequest('2'));
+ await handleSuccessfulRequest(
+ AnalysisUpdateContentParams(<String, Object>{
+ filePath: RemoveContentOverlay(),
+ }).toRequest('2'),
+ );
params = pluginManager.analysisUpdateContentParams!;
expect(params, isNotNull);
files = params.files;
@@ -258,15 +306,4 @@
overlay = files[filePath];
expect(overlay, const TypeMatcher<RemoveContentOverlay>());
}
-
- Future<List<String>> _getUserSources(AnalysisDriver driver) async {
- var sources = <String>[];
- await driver.applyPendingFileChanges();
- driver.addedFiles.forEach((path) {
- if (path.startsWith(convertPath('/User/'))) {
- sources.add(path);
- }
- });
- return sources;
- }
}
diff --git a/pkg/analysis_server/test/analysis_server_base.dart b/pkg/analysis_server/test/analysis_server_base.dart
index a6d5579..804f079 100644
--- a/pkg/analysis_server/test/analysis_server_base.dart
+++ b/pkg/analysis_server/test/analysis_server_base.dart
@@ -10,6 +10,7 @@
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/utilities/mocks.dart';
+import 'package:analyzer/dart/analysis/analysis_options.dart' as analysis;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
@@ -69,9 +70,13 @@
}
class PubPackageAnalysisServerTest with ResourceProviderMixin {
+ final TestPluginManager pluginManager = TestPluginManager();
late final MockServerChannel serverChannel;
late final AnalysisServer server;
+ final List<GeneralAnalysisService> _analysisGeneralServices = [];
+ final Map<AnalysisService, List<String>> _analysisFileSubscriptions = {};
+
AnalysisDomainHandler get analysisDomain {
return server.handlers.whereType<AnalysisDomainHandler>().single;
}
@@ -86,8 +91,15 @@
EnableString.super_parameters,
];
+ Folder get sdkRoot => newFolder('/sdk');
+
File get testFile => getFile(testFilePath);
+ analysis.AnalysisOptions get testFileAnalysisOptions {
+ var analysisDriver = server.getAnalysisDriver(testFile.path)!;
+ return analysisDriver.analysisOptions;
+ }
+
String get testFileContent => testFile.readAsStringSync();
String get testFilePath => '$testPackageLibPath/test.dart';
@@ -102,6 +114,25 @@
String get workspaceRootPath => '/home';
+ Future<void> addAnalysisSubscription(
+ AnalysisService service,
+ File file,
+ ) async {
+ (_analysisFileSubscriptions[service] ??= []).add(file.path);
+ await handleSuccessfulRequest(
+ AnalysisSetSubscriptionsParams(
+ _analysisFileSubscriptions,
+ ).toRequest('0'),
+ );
+ }
+
+ Future<void> addGeneralAnalysisSubscription(
+ GeneralAnalysisService service,
+ ) async {
+ _analysisGeneralServices.add(service);
+ await _setGeneralAnalysisSubscriptions();
+ }
+
/// TODO(scheglov) rename
void addTestFile(String content) {
newFile2(testFilePath, content);
@@ -144,6 +175,10 @@
return response;
}
+ void modifyTestFile(String content) {
+ modifyFile(testFilePath, content);
+ }
+
/// Returns the offset of [search] in [file].
/// Fails if not found.
int offsetInFile(File file, String search) {
@@ -155,6 +190,21 @@
void processNotification(Notification notification) {}
+ Future<void> removeGeneralAnalysisSubscription(
+ GeneralAnalysisService service,
+ ) async {
+ _analysisGeneralServices.remove(service);
+ await _setGeneralAnalysisSubscriptions();
+ }
+
+ void setPriorityFiles(List<File> files) {
+ handleSuccessfulRequest(
+ AnalysisSetPriorityFilesParams(
+ files.map((e) => e.path).toList(),
+ ).toRequest('0'),
+ );
+ }
+
Future<void> setRoots({
required List<String> included,
required List<String> excluded,
@@ -174,7 +224,6 @@
void setUp() {
serverChannel = MockServerChannel();
- var sdkRoot = newFolder('/sdk');
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
@@ -200,6 +249,7 @@
);
server.pendingFilesRemoveOverlayDelay = const Duration(milliseconds: 10);
+ server.pluginManager = pluginManager;
completionDomain.budgetDuration = const Duration(seconds: 30);
}
@@ -249,4 +299,12 @@
void writeTestPackagePubspecYamlFile(String content) {
newPubspecYamlFile(testPackageRootPath, content);
}
+
+ Future<void> _setGeneralAnalysisSubscriptions() async {
+ await handleSuccessfulRequest(
+ AnalysisSetGeneralSubscriptionsParams(
+ _analysisGeneralServices,
+ ).toRequest('0'),
+ );
+ }
}
diff --git a/pkg/analysis_server/test/edit/format_test.dart b/pkg/analysis_server/test/edit/format_test.dart
index d62a67e..742ae2a 100644
--- a/pkg/analysis_server/test/edit/format_test.dart
+++ b/pkg/analysis_server/test/edit/format_test.dart
@@ -4,7 +4,6 @@
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/edit/edit_domain.dart';
-import 'package:analysis_server/src/utilities/progress.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -26,44 +25,42 @@
handler = EditDomainHandler(server);
}
- Future test_format_longLine() {
+ Future<void> test_format_longLine() async {
var content = '''
fun(firstParam, secondParam, thirdParam, fourthParam) {
if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
}
''';
addTestFile(content);
- return waitForTasksFinished().then((_) {
- var formatResult = _formatAt(0, 3, lineLength: 100);
+ await waitForTasksFinished();
+ var formatResult = await _formatAt(0, 3, lineLength: 100);
- expect(formatResult.edits, isNotNull);
- expect(formatResult.edits, hasLength(0));
+ expect(formatResult.edits, isNotNull);
+ expect(formatResult.edits, hasLength(0));
- expect(formatResult.selectionOffset, equals(0));
- expect(formatResult.selectionLength, equals(3));
- });
+ expect(formatResult.selectionOffset, equals(0));
+ expect(formatResult.selectionLength, equals(3));
}
- Future test_format_noOp() {
+ Future<void> test_format_noOp() async {
// Already formatted source
addTestFile('''
main() {
int x = 3;
}
''');
- return waitForTasksFinished().then((_) {
- var formatResult = _formatAt(0, 3);
- expect(formatResult.edits, isNotNull);
- expect(formatResult.edits, hasLength(0));
- });
+ await waitForTasksFinished();
+ var formatResult = await _formatAt(0, 3);
+ expect(formatResult.edits, isNotNull);
+ expect(formatResult.edits, hasLength(0));
}
- Future test_format_noSelection() async {
+ Future<void> test_format_noSelection() async {
addTestFile('''
main() { int x = 3; }
''');
await waitForTasksFinished();
- var formatResult = _formatAt(0, 0);
+ var formatResult = await _formatAt(0, 0);
expect(formatResult.edits, isNotNull);
expect(formatResult.edits, hasLength(1));
@@ -78,44 +75,42 @@
expect(formatResult.selectionLength, equals(0));
}
- Future test_format_simple() {
+ Future<void> test_format_simple() async {
addTestFile('''
main() { int x = 3; }
''');
- return waitForTasksFinished().then((_) {
- var formatResult = _formatAt(0, 3);
+ await waitForTasksFinished();
+ var formatResult = await _formatAt(0, 3);
- expect(formatResult.edits, isNotNull);
- expect(formatResult.edits, hasLength(1));
+ expect(formatResult.edits, isNotNull);
+ expect(formatResult.edits, hasLength(1));
- var edit = formatResult.edits[0];
- expect(edit.replacement, equals('''
+ var edit = formatResult.edits[0];
+ expect(edit.replacement, equals('''
main() {
int x = 3;
}
'''));
- expect(formatResult.selectionOffset, equals(0));
- expect(formatResult.selectionLength, equals(3));
- });
+ expect(formatResult.selectionOffset, equals(0));
+ expect(formatResult.selectionLength, equals(3));
}
- Future test_format_withErrors() {
+ Future<void> test_format_withErrors() async {
addTestFile('''
main() { int x =
''');
- return waitForTasksFinished().then((_) {
- var request = EditFormatParams(testFile, 0, 3).toRequest('0');
- var response = handler.handleRequest(request, NotCancelableToken());
- expect(response, isResponseFailure('0'));
- });
+ await waitForTasksFinished();
+ var request = EditFormatParams(testFile, 0, 3).toRequest('0');
+ var response = await waitResponse(request);
+ expect(response, isResponseFailure('0'));
}
- EditFormatResult _formatAt(int selectionOffset, int selectionLength,
- {int? lineLength}) {
+ Future<EditFormatResult> _formatAt(int selectionOffset, int selectionLength,
+ {int? lineLength}) async {
var request = EditFormatParams(testFile, selectionOffset, selectionLength,
lineLength: lineLength)
.toRequest('0');
- var response = handleSuccessfulRequest(request);
+ var response = await waitResponse(request);
return EditFormatResult.fromResponse(response);
}
}
diff --git a/pkg/analysis_server/test/lsp/type_definition_test.dart b/pkg/analysis_server/test/lsp/type_definition_test.dart
index 9f003e8..b7c92ea 100644
--- a/pkg/analysis_server/test/lsp/type_definition_test.dart
+++ b/pkg/analysis_server/test/lsp/type_definition_test.dart
@@ -16,6 +16,11 @@
@reflectiveTest
class TypeDefinitionTest extends AbstractLspAnalysisServerTest {
+ Uri get sdkCoreUri {
+ final sdkCorePath = convertPath('/sdk/lib/core/core.dart');
+ return Uri.file(sdkCorePath);
+ }
+
Future<void> test_currentFile() async {
final contents = '''
class [[A]] {}
@@ -78,7 +83,7 @@
''';
final result = await _getLocationResult(contents);
- expect(result.uri, 'file:///sdk/lib/core/core.dart');
+ expect(result.uri, '$sdkCoreUri');
_expectNameRange(result.range, 'String');
}
@@ -264,7 +269,7 @@
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectSdkCoreType(LocationLink result, String typeName) {
- expect(result.targetUri, 'file:///sdk/lib/core/core.dart');
+ expect(result.targetUri, '$sdkCoreUri');
_expectNameRange(result.targetSelectionRange, typeName);
_expectCodeRange(result.targetRange);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
index 92c5799..783000a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
@@ -12,6 +12,9 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(CreateConstructorForFinalFieldsTest);
+ defineReflectiveTests(CreateConstructorForFinalFieldsWithoutNullSafetyTest);
+ defineReflectiveTests(
+ CreateConstructorForFinalFieldsWithoutSuperParametersTest);
});
}
@@ -45,7 +48,7 @@
class MyWidget extends StatelessWidget {
final int a;
final int b = 2;
- final int c;
+ final int? c;
}
''');
await assertHasFix('''
@@ -54,9 +57,9 @@
class MyWidget extends StatelessWidget {
final int a;
final int b = 2;
- final int c;
+ final int? c;
- const MyWidget({Key? key, this.a, this.c}) : super(key: key);
+ const MyWidget({super.key, required this.a, this.c});
}
''', errorFilter: (error) {
return error.message.contains("'a'");
@@ -71,7 +74,7 @@
class MyWidget extends StatelessWidget {
final int a;
final Widget child;
- final int b;
+ final int? b;
}
''');
await assertHasFix('''
@@ -80,9 +83,9 @@
class MyWidget extends StatelessWidget {
final int a;
final Widget child;
- final int b;
+ final int? b;
- const MyWidget({Key? key, this.a, this.b, this.child}) : super(key: key);
+ const MyWidget({super.key, required this.a, this.b, required this.child});
}
''', errorFilter: (error) {
return error.message.contains("'a'");
@@ -97,7 +100,7 @@
class MyWidget extends StatelessWidget {
final int a;
final List<Widget> children;
- final int b;
+ final int? b;
}
''');
await assertHasFix('''
@@ -106,9 +109,9 @@
class MyWidget extends StatelessWidget {
final int a;
final List<Widget> children;
- final int b;
+ final int? b;
- const MyWidget({Key? key, this.a, this.b, this.children}) : super(key: key);
+ const MyWidget({super.key, required this.a, this.b, required this.children});
}
''', errorFilter: (error) {
return error.message.contains("'a'");
@@ -175,3 +178,179 @@
await assertNoFix();
}
}
+
+@reflectiveTest
+class CreateConstructorForFinalFieldsWithoutNullSafetyTest
+ extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS;
+
+ @override
+ String get testPackageLanguageVersion => '2.9';
+
+ Future<void> test_flutter() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final int b = 2;
+ final int c;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final int b = 2;
+ final int c;
+
+ const MyWidget({Key key, this.a, this.c}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+
+ Future<void> test_flutter_childLast() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final Widget child;
+ final int b;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final Widget child;
+ final int b;
+
+ const MyWidget({Key key, this.a, this.b, this.child}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+
+ Future<void> test_flutter_childrenLast() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final List<Widget> children;
+ final int b;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final List<Widget> children;
+ final int b;
+
+ const MyWidget({Key key, this.a, this.b, this.children}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+}
+
+@reflectiveTest
+class CreateConstructorForFinalFieldsWithoutSuperParametersTest
+ extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS;
+
+ @override
+ String get testPackageLanguageVersion => '2.16';
+
+ Future<void> test_flutter() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final int b = 2;
+ final int? c;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final int b = 2;
+ final int? c;
+
+ const MyWidget({Key? key, required this.a, this.c}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+
+ Future<void> test_flutter_childLast() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final Widget child;
+ final int? b;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final Widget child;
+ final int? b;
+
+ const MyWidget({Key? key, required this.a, this.b, required this.child}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+
+ Future<void> test_flutter_childrenLast() async {
+ writeTestPackageConfig(flutter: true);
+ await resolveTestCode('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final List<Widget>? children;
+ final int? b;
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/widgets.dart';
+
+class MyWidget extends StatelessWidget {
+ final int a;
+ final List<Widget>? children;
+ final int? b;
+
+ const MyWidget({Key? key, required this.a, this.b, this.children}) : super(key: key);
+}
+''', errorFilter: (error) {
+ return error.message.contains("'a'");
+ });
+ }
+}
diff --git a/pkg/analyzer/lib/src/summary2/top_level_inference.dart b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
index 9708fbb..50db402 100644
--- a/pkg/analyzer/lib/src/summary2/top_level_inference.dart
+++ b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
@@ -203,7 +203,7 @@
if (_superParameters.isNotEmpty) {
dependencies.addIfNotNull(
- _walker.getNode(_constructor.superConstructor),
+ _walker.getNode(_constructor.superConstructor?.declaration),
);
}
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index 1a23546..b5bc665 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -1761,19 +1761,19 @@
}
test_class_constructor_parameters_super_requiredPositional_inferenceOrder() async {
- // It is important that `C` is declared after `B`, so that we check that
- // inference happens in order - first `C`, then `B`.
+ // It is important that `B` is declared after `C`, so that we check that
+ // inference happens in order - first `B`, then `C`.
var library = await buildLibrary('''
abstract class A {
A(int a);
}
-class B extends C {
- B(super.a);
+class C extends B {
+ C(super.a);
}
-class C extends A {
- C(super.a);
+class B extends A {
+ B(super.a);
}
''');
checkElementText(library, r'''
@@ -1786,16 +1786,16 @@
parameters
requiredPositional a @27
type: int
- class B @40
- supertype: C
+ class C @40
+ supertype: B
constructors
@56
parameters
requiredPositional final super.a @64
type: int
superConstructorParameter: a@101
- superConstructor: self::@class::C::@constructor::•
- class C @77
+ superConstructor: self::@class::B::@constructor::•
+ class B @77
supertype: A
constructors
@93
@@ -1807,6 +1807,60 @@
''');
}
+ test_class_constructor_parameters_super_requiredPositional_inferenceOrder_generic() async {
+ // It is important that `C` is declared before `B`, so that we check that
+ // inference happens in order - first `B`, then `C`.
+ var library = await buildLibrary('''
+class A {
+ A(int a);
+}
+
+class C extends B<String> {
+ C(super.a);
+}
+
+class B<T> extends A {
+ B(super.a);
+}
+''');
+ checkElementText(library, r'''
+library
+ definingUnit
+ classes
+ class A @6
+ constructors
+ @12
+ parameters
+ requiredPositional a @18
+ type: int
+ class C @31
+ supertype: B<String>
+ constructors
+ @55
+ parameters
+ requiredPositional final super.a @63
+ type: int
+ superConstructorParameter: ParameterMember
+ base: a@103
+ substitution: {T: String}
+ superConstructor: ConstructorMember
+ base: self::@class::B::@constructor::•
+ substitution: {T: String}
+ class B @76
+ typeParameters
+ covariant T @78
+ defaultType: dynamic
+ supertype: A
+ constructors
+ @95
+ parameters
+ requiredPositional final super.a @103
+ type: int
+ superConstructorParameter: a@18
+ superConstructor: self::@class::A::@constructor::•
+''');
+ }
+
test_class_constructor_parameters_super_requiredPositional_unresolved() async {
var library = await buildLibrary('''
class A {}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index fcdf267..029efeb 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -304,13 +304,9 @@
programSplitConstraintsData = constraintParser.read(programSplitJson);
}
- if (options.cfeOnly) {
- await runLoadKernel();
- } else if (options.modularMode) {
- await runModularAnalysis();
- } else {
+ await selfTask.measureSubtask("compileFromKernel", () async {
await runSequentialPhases();
- }
+ });
}
/// Clear the internal compiler state to prevent memory leaks when invoking
@@ -329,7 +325,13 @@
}
}
- JClosedWorld computeClosedWorld(Uri rootLibraryUri, Iterable<Uri> libraries) {
+ JClosedWorld computeClosedWorld(
+ ir.Component component,
+ List<ModuleData> moduleData,
+ Uri rootLibraryUri,
+ Iterable<Uri> libraries) {
+ frontendStrategy.registerLoadedLibraries(component, libraries);
+ frontendStrategy.registerModuleData(moduleData);
ResolutionEnqueuer resolutionEnqueuer = frontendStrategy
.createResolutionEnqueuer(enqueueTask, this)
..onEmptyForTesting = onResolutionQueueEmptyForTesting;
@@ -379,59 +381,83 @@
return closedWorld;
}
- Future<load_kernel.Output> runLoadKernel() async {
+ Future<load_kernel.Output> loadKernel() async {
final input = load_kernel.Input(options, provider, reporter,
initializedCompilerState, forceSerializationForTesting);
load_kernel.Output output =
await loadKernelTask.measure(() async => load_kernel.run(input));
- if (output == null || compilationFailed) return null;
reporter.log("Kernel load complete");
- if (retainDataForTesting) {
- componentForTesting = output.component;
- }
- if (options.features.newDumpInfo.isEnabled && options.dumpInfo) {
- untrimmedComponentForDumpInfo = output.component;
- }
-
- if (options.cfeOnly) {
- ir.Component component = output.component;
- dumpUnusedLibrariesAndTrimComponent(component, output.libraries);
- await serializationTask.serializeComponent(component);
- }
return output;
}
- void dumpUnusedLibrariesAndTrimComponent(
- ir.Component component, List<Uri> libraries) {
- if (options.fromDill) {
- if (options.dumpUnusedLibraries) {
- dumpUnusedLibraries(component, libraries);
+ Future<load_kernel.Output> produceKernel() async {
+ if (options.readClosedWorldUri == null) {
+ load_kernel.Output output = await loadKernel();
+ if (output == null || compilationFailed) return null;
+ ir.Component component = output.component;
+ if (retainDataForTesting) {
+ componentForTesting = component;
}
- if (options.entryUri != null) {
- component = trimComponent(component, libraries);
+ if (options.features.newDumpInfo.isEnabled && options.dumpInfo) {
+ untrimmedComponentForDumpInfo = component;
}
+ if (options.cfeOnly) {
+ if (options.fromDill) {
+ List<Uri> libraries = output.libraries;
+ if (options.dumpUnusedLibraries) {
+ dumpUnusedLibraries(component, libraries);
+ }
+ if (options.entryUri != null) {
+ component = trimComponent(component, libraries);
+ }
+ }
+ await serializationTask.serializeComponent(component);
+ }
+ // We currently do not return the trimmed component from [produceKernel]
+ // because downstream phases, in particular modular analysis, currently
+ // depend on the untrimmed dill.
+ return output;
+ } else {
+ ir.Component component =
+ await serializationTask.deserializeComponentAndUpdateOptions();
+ return load_kernel.Output(component, null, null, null, null);
}
}
- void runModularAnalysis() async {
- load_kernel.Output output = await runLoadKernel();
- if (output == null || compilationFailed) return;
+ bool shouldStopAfterLoadKernel(load_kernel.Output output) =>
+ output == null || compilationFailed || options.cfeOnly;
+ Future<ModuleData> runModularAnalysis(
+ load_kernel.Output output, Set<Uri> moduleLibraries) async {
ir.Component component = output.component;
List<Uri> libraries = output.libraries;
- Set<Uri> moduleLibraries = output.moduleLibraries.toSet();
_userCodeLocations
.addAll(moduleLibraries.map((module) => CodeLocation(module)));
final input = modular_analysis.Input(
options, reporter, environment, component, libraries, moduleLibraries);
- ModuleData moduleData = await selfTask.measureSubtask(
+ return await selfTask.measureSubtask(
'runModularAnalysis', () async => modular_analysis.run(input));
- if (compilationFailed) return;
- serializationTask.testModuleSerialization(moduleData, component);
- serializationTask.serializeModuleData(
- moduleData, component, moduleLibraries);
}
+ Future<List<ModuleData>> produceModuleData(load_kernel.Output output) async {
+ ir.Component component = output.component;
+ if (options.modularMode) {
+ Set<Uri> moduleLibraries = output.moduleLibraries.toSet();
+ ModuleData moduleData = await runModularAnalysis(output, moduleLibraries);
+ if (options.writeModularAnalysisUri != null && !compilationFailed) {
+ serializationTask.testModuleSerialization(moduleData, component);
+ serializationTask.serializeModuleData(
+ moduleData, component, moduleLibraries);
+ }
+ return [moduleData];
+ } else {
+ return await serializationTask.deserializeModuleData(component);
+ }
+ }
+
+ bool get shouldStopAfterModularAnalysis =>
+ compilationFailed || options.writeModularAnalysisUri != null;
+
GlobalTypeInferenceResults performGlobalTypeInference(
JClosedWorld closedWorld) {
FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction;
@@ -508,48 +534,23 @@
globalTypeInferenceResultsData);
}
- Future<load_kernel.Output> loadComponent() async {
- load_kernel.Output output = await runLoadKernel();
- if (output == null || compilationFailed) return null;
-
+ Future<ClosedWorldAndIndices> produceClosedWorld(
+ load_kernel.Output output, List<ModuleData> moduleData) async {
ir.Component component = output.component;
- List<Uri> libraries = output.libraries;
- frontendStrategy.registerLoadedLibraries(component, libraries);
- List<ModuleData> data;
- if (options.hasModularAnalysisInputs) {
- data = await serializationTask.deserializeModuleData(component);
- }
- frontendStrategy.registerModuleData(data);
-
- // After we've deserialized modular data, we trim the component of any
- // unnecessary dependencies.
- // Note: It is critical we wait to trim the dill until after we've
- // deserialized modular data because some of this data may reference
- // 'trimmed' elements.
- dumpUnusedLibrariesAndTrimComponent(component, libraries);
- return output;
- }
-
- Future<ClosedWorldAndIndices> produceClosedWorld() async {
ClosedWorldAndIndices closedWorldAndIndices;
if (options.readClosedWorldUri == null) {
- load_kernel.Output loadKernelOutput = await loadComponent();
- if (loadKernelOutput != null) {
- Uri rootLibraryUri = loadKernelOutput.rootLibraryUri;
- Iterable<Uri> libraries = loadKernelOutput.libraries;
- _userCodeLocations.add(CodeLocation(rootLibraryUri));
- JsClosedWorld closedWorld =
- computeClosedWorld(rootLibraryUri, libraries);
- closedWorldAndIndices = ClosedWorldAndIndices(closedWorld, null);
- if (options.writeClosedWorldUri != null) {
- serializationTask.serializeComponent(
- closedWorld.elementMap.programEnv.mainComponent);
- serializationTask.serializeClosedWorld(closedWorld);
- }
+ Uri rootLibraryUri = output.rootLibraryUri;
+ Iterable<Uri> libraries = output.libraries;
+ _userCodeLocations.add(CodeLocation(rootLibraryUri));
+ JsClosedWorld closedWorld =
+ computeClosedWorld(component, moduleData, rootLibraryUri, libraries);
+ closedWorldAndIndices = ClosedWorldAndIndices(closedWorld, null);
+ if (options.writeClosedWorldUri != null) {
+ serializationTask.serializeComponent(
+ closedWorld.elementMap.programEnv.mainComponent);
+ serializationTask.serializeClosedWorld(closedWorld);
}
} else {
- ir.Component component =
- await serializationTask.deserializeComponentAndUpdateOptions();
closedWorldAndIndices = await serializationTask.deserializeClosedWorld(
environment, abstractValueStrategy, component);
}
@@ -625,29 +626,40 @@
bool get shouldStopAfterCodegen => options.writeCodegenUri != null;
void runSequentialPhases() async {
- await selfTask.measureSubtask("compileFromKernel", () async {
- // Load kernel and compute closed world.
- ClosedWorldAndIndices closedWorldAndIndices = await produceClosedWorld();
- if (shouldStopAfterClosedWorld(closedWorldAndIndices)) return;
+ // Load kernel.
+ load_kernel.Output output = await produceKernel();
+ if (shouldStopAfterLoadKernel(output)) return;
- // Run global analysis.
- GlobalTypeInferenceResults globalTypeInferenceResults =
- await produceGlobalTypeInferenceResults(closedWorldAndIndices);
- if (shouldStopAfterGlobalTypeInference) return;
+ // Run modular analysis. This may be null if modular analysis was not
+ // requested for this pipeline.
+ List<ModuleData> moduleData;
+ if (options.modularMode || options.hasModularAnalysisInputs) {
+ moduleData = await produceModuleData(output);
+ }
+ if (shouldStopAfterModularAnalysis) return;
- // Run codegen.
- CodegenResults codegenResults = await produceCodegenResults(
- globalTypeInferenceResults, closedWorldAndIndices.indices);
- if (shouldStopAfterCodegen) return;
+ // Compute closed world.
+ ClosedWorldAndIndices closedWorldAndIndices =
+ await produceClosedWorld(output, moduleData);
+ if (shouldStopAfterClosedWorld(closedWorldAndIndices)) return;
- // Link.
- int programSize = runCodegenEnqueuer(codegenResults);
+ // Run global analysis.
+ GlobalTypeInferenceResults globalTypeInferenceResults =
+ await produceGlobalTypeInferenceResults(closedWorldAndIndices);
+ if (shouldStopAfterGlobalTypeInference) return;
- // Dump Info.
- if (options.dumpInfo) {
- runDumpInfo(codegenResults, programSize);
- }
- });
+ // Run codegen.
+ CodegenResults codegenResults = await produceCodegenResults(
+ globalTypeInferenceResults, closedWorldAndIndices.indices);
+ if (shouldStopAfterCodegen) return;
+
+ // Link.
+ int programSize = runCodegenEnqueuer(codegenResults);
+
+ // Dump Info.
+ if (options.dumpInfo) {
+ runDumpInfo(codegenResults, programSize);
+ }
}
void runDumpInfo(CodegenResults codegenResults, int programSize) {
diff --git a/pkg/dart2wasm/bin/run_wasm.js b/pkg/dart2wasm/bin/run_wasm.js
index b5034ea..593f159 100644
--- a/pkg/dart2wasm/bin/run_wasm.js
+++ b/pkg/dart2wasm/bin/run_wasm.js
@@ -42,6 +42,39 @@
}
}
+// Converts a JS array to a Dart List, and also recursively converts the items
+// in the array.
+function arrayToDartList(array, allocator, adder) {
+ var length = array.length;
+ var dartList = dartInstance.exports.$listAllocate();
+ for (var i = 0; i < length; i++) {
+ dartInstance.exports.$listAdd(dartList, array[i]);
+ }
+ return dartList;
+}
+
+// Converts a Dart List to a JS array. Any Dart objects will be converted, but
+// this will be cheap for JSValues.
+function arrayFromDartList(list, reader) {
+ var length = dartInstance.exports.$listLength(list);
+ var array = new Array(length);
+ for (var i = 0; i < length; i++) {
+ array[i] = dartInstance.exports.$listRead(list, i);
+ }
+ return array;
+}
+
+// Recursively converts a JS object into a Dart object.
+function dartify(object) {
+ if (typeof object === "string") {
+ return stringToDartString(object);
+ } else if (object instanceof Array) {
+ return arrayToDartList(object);
+ } else {
+ return object;
+ }
+}
+
// Imports for printing and event loop
var dart2wasm = {
printToConsole: function(string) {
@@ -64,6 +97,41 @@
// stack trace.
let userStackString = stackString.split('\n').slice(3).join('\n');
return stringToDartString(userStackString);
+ },
+ arrayFromDartList: arrayFromDartList,
+ arrayToDartList: arrayToDartList,
+ stringFromDartString: stringFromDartString,
+ stringToDartString: stringToDartString,
+ dartify: dartify,
+ newObject: function() {
+ return {};
+ },
+ globalThis: function() {
+ return globalThis;
+ },
+ getProperty: function(object, name) {
+ return object[name];
+ },
+ hasProperty: function(object, name) {
+ return name in object;
+ },
+ setProperty: function(object, name, value) {
+ return object[name] = value;
+ },
+ callMethodVarArgs: function(object, name, args) {
+ return object[name].apply(object, args);
+ },
+ callConstructorVarArgs: function(object, name, args) {
+ // Gets a constructor property at object[name], and apply bind to the
+ // constructor. We pass `null` as the first argument to `bind.apply`
+ // because this is `bind`'s unused context argument(`new` will
+ // explicitly create a new context).
+ var constructor = object[name];
+ var factoryFunction = constructor.bind.apply(constructor, [null, ...args]);
+ return new factoryFunction();
+ },
+ eval: function(string) {
+ eval(string);
}
};
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 658a618..6631460 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -1181,7 +1181,7 @@
@override
w.ValueType visitEqualsNull(EqualsNull node, w.ValueType expectedType) {
- wrap(node.expression, translator.topInfo.nullableType);
+ wrap(node.expression, const w.RefType.any());
b.ref_is_null();
return w.NumType.i32;
}
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index 15e6651..cb5310be 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -46,6 +46,7 @@
'dart:_internal',
'dart:typed_data',
'dart:nativewrappers',
+ 'dart:js_util_wasm',
];
@override
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 4bb0a08..2f58bb1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -479,9 +479,6 @@
constantEvaluator.withNewEnvironment(() {
transformAnnotations(node.annotations, node);
transformTypeParameterList(node.typeParameters, node);
- transformTypeParameterList(node.typeParametersOfFunctionType, node);
- transformVariableDeclarationList(node.positionalParameters, node);
- transformVariableDeclarationList(node.namedParameters, node);
});
return node;
}
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 86e5832..5bbfd4a 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -299,24 +299,7 @@
// hood, so we only need the offset of the first annotation.
Token metadataToken = tokenForOffset(
typedefKeyword, endToken, metadata[0].charOffset)!;
- List<Expression> annotations =
- parseMetadata(typedefBuilder, metadataToken, null)!;
- if (formal.isPositional) {
- VariableDeclaration parameter =
- typedefBuilder.typedef.positionalParameters[i];
- for (Expression annotation in annotations) {
- parameter.addAnnotation(annotation);
- }
- } else {
- for (VariableDeclaration named
- in typedefBuilder.typedef.namedParameters) {
- if (named.name == formal.name) {
- for (Expression annotation in annotations) {
- named.addAnnotation(annotation);
- }
- }
- }
- }
+ parseMetadata(typedefBuilder, metadataToken, null)!;
}
}
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
index 638253e..f6fd8b7 100644
--- a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
@@ -7,37 +7,27 @@
import 'package:front_end/src/fasta/kernel/expression_generator_helper.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
-
-import 'package:kernel/type_algebra.dart'
- show FreshTypeParameters, getFreshTypeParameters;
-
import 'package:kernel/type_environment.dart';
-import '../fasta_codes.dart'
- show noLength, templateCyclicTypedef, templateTypeArgumentMismatch;
-
-import '../problems.dart' show unhandled;
-import '../scope.dart';
-
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/fixed_type_builder.dart';
-import '../builder/formal_parameter_builder.dart';
import '../builder/function_type_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/named_type_builder.dart';
-import '../builder/type_builder.dart';
import '../builder/type_alias_builder.dart';
+import '../builder/type_builder.dart';
import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
-
+import '../fasta_codes.dart'
+ show noLength, templateCyclicTypedef, templateTypeArgumentMismatch;
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/kernel_helper.dart';
-
+import '../problems.dart' show unhandled;
+import '../scope.dart';
import '../util/helpers.dart';
-
import 'source_library_builder.dart' show SourceLibraryBuilder;
class SourceTypeAliasBuilder extends TypeAliasBuilderImpl {
@@ -101,33 +91,9 @@
typedef.type ??= buildThisType();
TypeBuilder? type = this.type;
- if (type is FunctionTypeBuilder) {
- List<TypeParameter> typeParameters = new List<TypeParameter>.generate(
- type.typeVariables?.length ?? 0,
- (int i) => type.typeVariables![i].parameter,
- growable: false);
- FreshTypeParameters freshTypeParameters =
- getFreshTypeParameters(typeParameters);
- for (int i = 0; i < freshTypeParameters.freshTypeParameters.length; i++) {
- TypeParameter typeParameter =
- freshTypeParameters.freshTypeParameters[i];
- typedef.typeParametersOfFunctionType
- .add(typeParameter..parent = typedef);
- }
-
- if (type.formals != null) {
- for (FormalParameterBuilder formal in type.formals!) {
- VariableDeclaration parameter = formal.build(libraryBuilder, 0);
- parameter.type = freshTypeParameters.substitute(parameter.type);
- if (formal.isNamed) {
- typedef.namedParameters.add(parameter);
- } else {
- typedef.positionalParameters.add(parameter);
- }
- parameter.parent = typedef;
- }
- }
- } else if (type is NamedTypeBuilder || type is FixedTypeBuilder) {
+ if (type is FunctionTypeBuilder ||
+ type is NamedTypeBuilder ||
+ type is FixedTypeBuilder) {
// No error, but also no additional setup work.
// ignore: unnecessary_null_comparison
} else if (type != null) {
diff --git a/pkg/front_end/test/fasta/expression_suite.dart b/pkg/front_end/test/fasta/expression_suite.dart
index e8871b6..2e0a4a9 100644
--- a/pkg/front_end/test/fasta/expression_suite.dart
+++ b/pkg/front_end/test/fasta/expression_suite.dart
@@ -73,6 +73,7 @@
class Context extends ChainContext {
final CompilerContext compilerContext;
+ final CompilerContext compilerContextNoNNBD;
final List<DiagnosticMessage> errors;
@override
@@ -82,7 +83,8 @@
final Set<Uri> fuzzedLibraries = {};
int fuzzCompiles = 0;
- Context(this.compilerContext, this.errors, bool updateExpectations, this.fuzz)
+ Context(this.compilerContext, this.compilerContextNoNNBD, this.errors,
+ bool updateExpectations, this.fuzz)
: steps = <Step>[
const ReadTest(),
const CompileExpression(),
@@ -362,8 +364,12 @@
// Compile [test.expression], update [test.errors] with results.
// As a side effect - verify that generated procedure can be serialized.
- Future<void> compileExpression(TestCase test, IncrementalCompiler compiler,
- IncrementalCompilerResult compilerResult, Context context) async {
+ Future<void> compileExpression(
+ TestCase test,
+ IncrementalCompiler compiler,
+ IncrementalCompiler? compilerNoNNBD,
+ IncrementalCompilerResult compilerResult,
+ Context context) async {
Map<String, DartType>? definitions = createDefinitionsWithTypes(
compilerResult.classHierarchy?.knownLibraries,
test.definitionTypes,
@@ -408,29 +414,36 @@
}
if (context.fuzz) {
- await fuzz(compiler, compilerResult, context);
+ await fuzz(compiler, compilerNoNNBD!, compilerResult, context);
}
}
- Future<void> fuzz(IncrementalCompiler compiler,
- IncrementalCompilerResult compilerResult, Context context) async {
+ Future<void> fuzz(
+ IncrementalCompiler compiler,
+ IncrementalCompiler compilerNoNNBD,
+ IncrementalCompilerResult compilerResult,
+ Context context) async {
for (Library lib in compilerResult.classHierarchy!.knownLibraries) {
if (!context.fuzzedLibraries.add(lib.importUri)) continue;
for (Member m in lib.members) {
- await fuzzMember(m, compiler, lib.importUri, context);
+ await fuzzMember(m, compiler, compilerNoNNBD, lib.importUri, context);
}
for (Class c in lib.classes) {
for (Member m in c.members) {
- await fuzzMember(m, compiler, lib.importUri, context);
+ await fuzzMember(m, compiler, compilerNoNNBD, lib.importUri, context);
}
}
}
}
- Future<void> fuzzMember(Member m, IncrementalCompiler compiler,
- Uri libraryUri, Context context) async {
+ Future<void> fuzzMember(
+ Member m,
+ IncrementalCompiler compiler,
+ IncrementalCompiler compilerNoNNBD,
+ Uri libraryUri,
+ Context context) async {
String expression = m.name.text;
if (m is Field || (m is Procedure && m.isGetter)) {
// fields and getters are fine as-is
@@ -447,8 +460,7 @@
expression = "${parent.name}()";
}
} else {
- print("Ignoring $m (${m.runtimeType})");
- return;
+ throw "Didn't know ${m.runtimeType}";
}
String? className;
@@ -457,23 +469,36 @@
className = parent.name;
}
- await fuzzTryCompile(compiler, "$expression", libraryUri, className,
- !m.isInstanceMember, context);
- if (className != null && !m.isInstanceMember) {
- await fuzzTryCompile(compiler, "$className.$expression", libraryUri, null,
- !m.isInstanceMember, context);
- }
- await fuzzTryCompile(compiler, "$expression.toString()", libraryUri,
+ await fuzzTryCompile(compiler, compilerNoNNBD, "$expression", libraryUri,
className, !m.isInstanceMember, context);
if (className != null && !m.isInstanceMember) {
- await fuzzTryCompile(compiler, "$className.$expression.toString()",
+ await fuzzTryCompile(compiler, compilerNoNNBD, "$className.$expression",
libraryUri, null, !m.isInstanceMember, context);
}
- await fuzzTryCompile(compiler, "$expression.toString() == '42'", libraryUri,
- className, !m.isInstanceMember, context);
+ await fuzzTryCompile(compiler, compilerNoNNBD, "$expression.toString()",
+ libraryUri, className, !m.isInstanceMember, context);
if (className != null && !m.isInstanceMember) {
await fuzzTryCompile(
compiler,
+ compilerNoNNBD,
+ "$className.$expression.toString()",
+ libraryUri,
+ null,
+ !m.isInstanceMember,
+ context);
+ }
+ await fuzzTryCompile(
+ compiler,
+ compilerNoNNBD,
+ "$expression.toString() == '42'",
+ libraryUri,
+ className,
+ !m.isInstanceMember,
+ context);
+ if (className != null && !m.isInstanceMember) {
+ await fuzzTryCompile(
+ compiler,
+ compilerNoNNBD,
"$className.$expression.toString() == '42'",
libraryUri,
null,
@@ -482,6 +507,7 @@
}
await fuzzTryCompile(
compiler,
+ compilerNoNNBD,
"() { var x = $expression.toString(); x == '42'; }()",
libraryUri,
className,
@@ -490,6 +516,7 @@
if (className != null && !m.isInstanceMember) {
await fuzzTryCompile(
compiler,
+ compilerNoNNBD,
"() { var x = $className.$expression.toString(); x == '42'; }()",
libraryUri,
null,
@@ -498,25 +525,50 @@
}
}
- Future<void> fuzzTryCompile(IncrementalCompiler compiler, String expression,
- Uri libraryUri, String? className, bool isStatic, Context context) async {
+ Future<void> fuzzTryCompile(
+ IncrementalCompiler compiler,
+ IncrementalCompiler compilerNoNNBD,
+ String expression,
+ Uri libraryUri,
+ String? className,
+ bool isStatic,
+ Context context) async {
context.fuzzCompiles++;
print("Fuzz compile #${context.fuzzCompiles} "
"('$expression' in $libraryUri $className)");
- Procedure? compiledProcedure = await compiler.compileExpression(
- expression,
- {},
- [],
- "debugExpr",
- libraryUri,
- className: className,
- isStatic: isStatic,
- );
- context.takeErrors();
- if (compiledProcedure != null) {
- // Confirm we can serialize generated procedure.
- List<int> list = serializeProcedure(compiledProcedure);
- assert(list.length > 0);
+ {
+ Procedure? compiledProcedure = await compiler.compileExpression(
+ expression,
+ {},
+ [],
+ "debugExpr",
+ libraryUri,
+ className: className,
+ isStatic: isStatic,
+ );
+ context.takeErrors();
+ if (compiledProcedure != null) {
+ // Confirm we can serialize generated procedure.
+ List<int> list = serializeProcedure(compiledProcedure);
+ assert(list.length > 0);
+ }
+ }
+ {
+ Procedure? compiledProcedure = await compilerNoNNBD.compileExpression(
+ expression,
+ {},
+ [],
+ "debugExpr",
+ libraryUri,
+ className: className,
+ isStatic: isStatic,
+ );
+ context.takeErrors();
+ if (compiledProcedure != null) {
+ // Confirm we can serialize generated procedure.
+ List<int> list = serializeProcedure(compiledProcedure);
+ assert(list.length > 0);
+ }
}
}
@@ -543,11 +595,27 @@
"${errors.map((e) => e.plainTextFormatted.first).toList()}");
}
Uri dillFileUri = toTestUri("${test.description.shortName}.dill");
+ Uri dillFileNoNNBDUri =
+ toTestUri("${test.description.shortName}.no.nnbd.dill");
Uint8List dillData = await serializeComponent(component);
context.fileSystem.entityForUri(dillFileUri).writeAsBytesSync(dillData);
Set<Uri> beforeFuzzedLibraries = context.fuzzedLibraries.toSet();
- await compileExpression(
- test, sourceCompiler, sourceCompilerResult, context);
+ IncrementalCompiler? sourceCompilerNoNNBD;
+ if (context.fuzz) {
+ sourceCompilerNoNNBD =
+ new IncrementalCompiler(context.compilerContextNoNNBD);
+ IncrementalCompilerResult sourceCompilerNoNNBDResult =
+ await sourceCompilerNoNNBD
+ .computeDelta(entryPoints: [test.entryPoint]);
+ Component componentNoNNBD = sourceCompilerNoNNBDResult.component;
+ Uint8List dillDataNoNNBD = await serializeComponent(componentNoNNBD);
+ context.fileSystem
+ .entityForUri(dillFileNoNNBDUri)
+ .writeAsBytesSync(dillDataNoNNBD);
+ context.takeErrors();
+ }
+ await compileExpression(test, sourceCompiler, sourceCompilerNoNNBD,
+ sourceCompilerResult, context);
IncrementalCompiler dillCompiler =
new IncrementalCompiler(context.compilerContext, dillFileUri);
@@ -560,9 +628,23 @@
// Since it compiled successfully from source, the bootstrap-from-Dill
// should also succeed without errors.
assert(errors.isEmpty);
+
+ IncrementalCompiler? dillCompilerNoNNBD;
+ if (context.fuzz) {
+ dillCompilerNoNNBD = new IncrementalCompiler(
+ context.compilerContextNoNNBD, dillFileNoNNBDUri);
+ IncrementalCompilerResult dillCompilerNoNNBDResult =
+ await dillCompilerNoNNBD
+ .computeDelta(entryPoints: [test.entryPoint]);
+ Component componentNoNNBD = dillCompilerNoNNBDResult.component;
+ componentNoNNBD.computeCanonicalNames();
+ context.takeErrors();
+ }
+
context.fuzzedLibraries.clear();
context.fuzzedLibraries.addAll(beforeFuzzedLibraries);
- await compileExpression(test, dillCompiler, dillCompilerResult, context);
+ await compileExpression(
+ test, dillCompiler, dillCompilerNoNNBD, dillCompilerResult, context);
}
return new Result.pass(tests);
}
@@ -614,17 +696,37 @@
final ProcessedOptions options =
new ProcessedOptions(options: optionBuilder, inputs: [entryPoint]);
+ final CompilerOptions optionBuilderNoNNBD = new CompilerOptions()
+ ..target = new VmTarget(new TargetFlags())
+ ..verbose = true
+ ..omitPlatform = true
+ ..fileSystem = fs
+ ..sdkSummary = sdkSummary
+ ..onDiagnostic = (DiagnosticMessage message) {
+ printDiagnosticMessage(message, print);
+ errors.add(message);
+ }
+ ..environmentDefines = const {}
+ ..explicitExperimentalFlags = {ExperimentalFlag.nonNullable: false}
+ ..allowedExperimentalFlagsForTesting = const AllowedExperimentalFlags();
+
+ final ProcessedOptions optionsNoNNBD =
+ new ProcessedOptions(options: optionBuilderNoNNBD, inputs: [entryPoint]);
+
final bool updateExpectations = environment["updateExpectations"] == "true";
final bool fuzz = environment["fuzz"] == "true";
final CompilerContext compilerContext = new CompilerContext(options);
+ final CompilerContext compilerContextNoNNBD =
+ new CompilerContext(optionsNoNNBD);
// Disable colors to ensure that expectation files are the same across
// platforms and independent of stdin/stderr.
colors.enableColors = false;
- return new Context(compilerContext, errors, updateExpectations, fuzz);
+ return new Context(
+ compilerContext, compilerContextNoNNBD, errors, updateExpectations, fuzz);
}
void main([List<String> arguments = const []]) =>
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 5b21bf7..a85eeaf 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -245,6 +245,7 @@
connectivity
consideration
constness
+constraining
consult
consumers
container
@@ -320,6 +321,7 @@
demangle
demangled
dep
+depended
dependency's
deps
dereferencing
@@ -1024,6 +1026,7 @@
recompiling
recompute
recomputed
+reconciliation
reconstruct
recorder
recoveries
@@ -1038,6 +1041,7 @@
reexports
ref
refactoring
+refined
reflect
reflectee
reflective
@@ -1214,6 +1218,7 @@
stability
stacktrace
stacktraces
+stages
stale
stand
starter
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.expect b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.expect
index 44bd339..3f168f4 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.expect
@@ -2,8 +2,8 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int x, core::num y, {@#C2 @#C3 core::String z, core::Object w}) → void;
-typedef G = (@#C1 core::int a, core::num b, [@#C2 @#C3 core::String c, core::Object d]) → void;
+typedef F = (core::int, core::num, {w: core::Object, z: core::String}) → void;
+typedef G = (core::int, core::num, [core::String, core::Object]) → void;
static const field core::int foo = #C1;
static const field core::int bar = #C2;
static const field core::int baz = #C3;
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.modular.expect b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.modular.expect
index 44bd339..3f168f4 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.modular.expect
@@ -2,8 +2,8 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int x, core::num y, {@#C2 @#C3 core::String z, core::Object w}) → void;
-typedef G = (@#C1 core::int a, core::num b, [@#C2 @#C3 core::String c, core::Object d]) → void;
+typedef F = (core::int, core::num, {w: core::Object, z: core::String}) → void;
+typedef G = (core::int, core::num, [core::String, core::Object]) → void;
static const field core::int foo = #C1;
static const field core::int bar = #C2;
static const field core::int baz = #C3;
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.transformed.expect b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.transformed.expect
index 44bd339..3f168f4 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals.dart.weak.transformed.expect
@@ -2,8 +2,8 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int x, core::num y, {@#C2 @#C3 core::String z, core::Object w}) → void;
-typedef G = (@#C1 core::int a, core::num b, [@#C2 @#C3 core::String c, core::Object d]) → void;
+typedef F = (core::int, core::num, {w: core::Object, z: core::String}) → void;
+typedef G = (core::int, core::num, [core::String, core::Object]) → void;
static const field core::int foo = #C1;
static const field core::int bar = #C2;
static const field core::int baz = #C3;
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.expect b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.expect
index 95d7ed5..707fc20 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int app) → core::int;
+typedef F = (core::int) → core::int;
static const field core::int app = #C1;
static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.modular.expect b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.modular.expect
index 95d7ed5..707fc20 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.modular.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int app) → core::int;
+typedef F = (core::int) → core::int;
static const field core::int app = #C1;
static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.transformed.expect b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.transformed.expect
index 95d7ed5..707fc20 100644
--- a/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/annotation_typedef_formals_resolution.dart.weak.transformed.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef F = (@#C1 core::int app) → core::int;
+typedef F = (core::int) → core::int;
static const field core::int app = #C1;
static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.expect b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.expect
index d111b25..8329d57 100644
--- a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.expect
+++ b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef hest_t = ({@#C1 dynamic named}) → dynamic;
+typedef hest_t = ({named: dynamic}) → dynamic;
class Bar extends core::Object /*hasConstConstructor*/ {
const constructor •() → self::Bar
: super core::Object::•()
diff --git a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.modular.expect b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.modular.expect
index d111b25..8329d57 100644
--- a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.modular.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef hest_t = ({@#C1 dynamic named}) → dynamic;
+typedef hest_t = ({named: dynamic}) → dynamic;
class Bar extends core::Object /*hasConstConstructor*/ {
const constructor •() → self::Bar
: super core::Object::•()
diff --git a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.transformed.expect b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.transformed.expect
index d111b25..8329d57 100644
--- a/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/annotation_variable_declaration.dart.weak.transformed.expect
@@ -2,7 +2,7 @@
import self as self;
import "dart:core" as core;
-typedef hest_t = ({@#C1 dynamic named}) → dynamic;
+typedef hest_t = ({named: dynamic}) → dynamic;
class Bar extends core::Object /*hasConstConstructor*/ {
const constructor •() → self::Bar
: super core::Object::•()
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.expect
index 95a7fb9..41d3f4f 100644
--- a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.expect
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.expect
@@ -11,13 +11,13 @@
@#C1
part redirecting_factory_invocation_metadata_lib.dart;
@#C1
-typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t1, [@#C1 self::Class<dynamic> #t2]) → dynamic;
+typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, [self::Class<dynamic>]) → dynamic;
@#C1
-typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t3, {@#C1 self::Class<dynamic> o2}) → dynamic;
+typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, {o2: self::Class<dynamic>}) → dynamic;
@#C1
-typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, [@#C1 dynamic o2]) → void;
+typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, [dynamic]) → void;
@#C1
-typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, {@#C1 dynamic o2}) → void;
+typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, {o2: dynamic}) → void;
@#C1
class Const extends core::Object /*hasConstConstructor*/ {
static final field dynamic _redirecting# = <dynamic>[#C2]/*isLegacy*/;
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.modular.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.modular.expect
index 95a7fb9..41d3f4f 100644
--- a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.modular.expect
@@ -11,13 +11,13 @@
@#C1
part redirecting_factory_invocation_metadata_lib.dart;
@#C1
-typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t1, [@#C1 self::Class<dynamic> #t2]) → dynamic;
+typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, [self::Class<dynamic>]) → dynamic;
@#C1
-typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t3, {@#C1 self::Class<dynamic> o2}) → dynamic;
+typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, {o2: self::Class<dynamic>}) → dynamic;
@#C1
-typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, [@#C1 dynamic o2]) → void;
+typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, [dynamic]) → void;
@#C1
-typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, {@#C1 dynamic o2}) → void;
+typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, {o2: dynamic}) → void;
@#C1
class Const extends core::Object /*hasConstConstructor*/ {
static final field dynamic _redirecting# = <dynamic>[#C2]/*isLegacy*/;
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.transformed.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.transformed.expect
index 95a7fb9..41d3f4f 100644
--- a/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_metadata.dart.weak.transformed.expect
@@ -11,13 +11,13 @@
@#C1
part redirecting_factory_invocation_metadata_lib.dart;
@#C1
-typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t1, [@#C1 self::Class<dynamic> #t2]) → dynamic;
+typedef Typedef1<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, [self::Class<dynamic>]) → dynamic;
@#C1
-typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(@#C1 self::Class<dynamic> #t3, {@#C1 self::Class<dynamic> o2}) → dynamic;
+typedef Typedef2<@#C1 unrelated T extends core::Object? = dynamic> = <T extends core::Object? = dynamic>(self::Class<dynamic>, {o2: self::Class<dynamic>}) → dynamic;
@#C1
-typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, [@#C1 dynamic o2]) → void;
+typedef Typedef3<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, [dynamic]) → void;
@#C1
-typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (@#C1 dynamic o1, {@#C1 dynamic o2}) → void;
+typedef Typedef4<@#C1 unrelated T extends core::Object? = dynamic> = (dynamic, {o2: dynamic}) → void;
@#C1
class Const extends core::Object /*hasConstConstructor*/ {
static final field dynamic _redirecting# = <dynamic>[#C2]/*isLegacy*/;
diff --git a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.expect b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.expect
index 50a886c..3444c42 100644
--- a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.expect
+++ b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.expect
@@ -7,7 +7,6 @@
// ^
//
import self as self;
-import "dart:core" as core;
typedef Handle = invalid-type;
static method main() → dynamic {
diff --git a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.modular.expect b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.modular.expect
index 50a886c..3444c42 100644
--- a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.modular.expect
@@ -7,7 +7,6 @@
// ^
//
import self as self;
-import "dart:core" as core;
typedef Handle = invalid-type;
static method main() → dynamic {
diff --git a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.outline.expect b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.outline.expect
index 74c6c6a..dc3f69b 100644
--- a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.outline.expect
@@ -7,7 +7,6 @@
// ^
//
import self as self;
-import "dart:core" as core;
typedef Handle = invalid-type;
static method main() → dynamic
diff --git a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.transformed.expect b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.transformed.expect
index 50a886c..3444c42 100644
--- a/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/rasta/malformed_function_type.dart.weak.transformed.expect
@@ -7,7 +7,6 @@
// ^
//
import self as self;
-import "dart:core" as core;
typedef Handle = invalid-type;
static method main() → dynamic {
diff --git a/pkg/front_end/tool/ast_model.dart b/pkg/front_end/tool/ast_model.dart
index e06996a..9979dcb 100644
--- a/pkg/front_end/tool/ast_model.dart
+++ b/pkg/front_end/tool/ast_model.dart
@@ -128,9 +128,6 @@
},
'Typedef': {
'typeParameters': FieldRule(isDeclaration: true),
- 'typeParametersOfFunctionType': FieldRule(isDeclaration: false),
- 'positionalParameters': FieldRule(isDeclaration: false),
- 'namedParameters': FieldRule(isDeclaration: false),
},
'TypedefTearOff': {
'typeParameters': FieldRule(isDeclaration: true),
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 3df728f..b3f1a41 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 76;
+ UInt32 formatVersion = 77;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
@@ -288,9 +288,6 @@
List<Expression> annotations;
List<TypeParameter> typeParameters;
DartType type;
- List<TypeParameter> typeParametersOfFunctionType;
- List<VariableDeclarationPlain> positionalParameters;
- List<VariableDeclarationPlain> namedParameters;
}
type Combinator {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 17ac253..c98194e 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -840,14 +840,6 @@
// TODO(johnniwinther): Make this non-nullable.
DartType? type;
- // The following fields describe parameters of the underlying type when
- // that is a function type. They are needed to keep such attributes as names
- // and annotations. When the underlying type is not a function type, they are
- // empty.
- final List<TypeParameter> typeParametersOfFunctionType;
- final List<VariableDeclaration> positionalParameters;
- final List<VariableDeclaration> namedParameters;
-
Typedef(this.name, this.type,
{Reference? reference,
required this.fileUri,
@@ -858,16 +850,8 @@
// ignore: unnecessary_null_comparison
: assert(fileUri != null),
this.typeParameters = typeParameters ?? <TypeParameter>[],
- this.typeParametersOfFunctionType =
- typeParametersOfFunctionType ?? <TypeParameter>[],
- this.positionalParameters =
- positionalParameters ?? <VariableDeclaration>[],
- this.namedParameters = namedParameters ?? <VariableDeclaration>[],
super(reference) {
setParents(this.typeParameters, this);
- setParents(this.typeParametersOfFunctionType, this);
- setParents(this.positionalParameters, this);
- setParents(this.namedParameters, this);
}
Library get enclosingLibrary => parent as Library;
@@ -883,9 +867,6 @@
visitList(annotations, v);
visitList(typeParameters, v);
type?.accept(v);
- visitList(typeParametersOfFunctionType, v);
- visitList(positionalParameters, v);
- visitList(namedParameters, v);
}
@override
@@ -895,9 +876,6 @@
if (type != null) {
type = v.visitDartType(type!);
}
- v.transformList(typeParametersOfFunctionType, this);
- v.transformList(positionalParameters, this);
- v.transformList(namedParameters, this);
}
@override
@@ -912,9 +890,6 @@
type = newType;
}
}
- v.transformTypeParameterList(typeParametersOfFunctionType, this);
- v.transformVariableDeclarationList(positionalParameters, this);
- v.transformVariableDeclarationList(namedParameters, this);
}
@override
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index e067807..8952e40 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1340,13 +1340,6 @@
node.annotations = readAnnotationList(node);
readAndPushTypeParameterList(node.typeParameters, node);
DartType type = readDartType();
- readAndPushTypeParameterList(node.typeParametersOfFunctionType, node);
- node.positionalParameters.clear();
- node.positionalParameters.addAll(readAndPushVariableDeclarationList());
- setParents(node.positionalParameters, node);
- node.namedParameters.clear();
- node.namedParameters.addAll(readAndPushVariableDeclarationList());
- setParents(node.namedParameters, node);
typeParameterStack.length = 0;
variableStack.length = 0;
node.fileOffset = fileOffset;
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index dc9ece8..c048177 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1166,12 +1166,6 @@
writeNodeList(node.typeParameters);
writeNode(node.type!);
- enterScope(typeParameters: node.typeParametersOfFunctionType);
- writeNodeList(node.typeParametersOfFunctionType);
- writeVariableDeclarationList(node.positionalParameters);
- writeVariableDeclarationList(node.namedParameters);
-
- leaveScope(typeParameters: node.typeParametersOfFunctionType);
leaveScope(typeParameters: node.typeParameters, variableScope: true);
leaveScope(memberScope: true);
}
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index a8a29ff..39dbd8c 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -176,7 +176,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 76;
+ static const int BinaryFormatVersion = 77;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart
index 9e8ae3f..3f11abc 100644
--- a/pkg/kernel/lib/src/equivalence.dart
+++ b/pkg/kernel/lib/src/equivalence.dart
@@ -1436,15 +1436,6 @@
if (!checkTypedef_type(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
- if (!checkTypedef_typeParametersOfFunctionType(visitor, node, other)) {
- result = visitor.resultOnInequivalence;
- }
- if (!checkTypedef_positionalParameters(visitor, node, other)) {
- result = visitor.resultOnInequivalence;
- }
- if (!checkTypedef_namedParameters(visitor, node, other)) {
- result = visitor.resultOnInequivalence;
- }
if (!checkTypedef_reference(visitor, node, other)) {
result = visitor.resultOnInequivalence;
}
@@ -4634,30 +4625,6 @@
return visitor.checkNodes(node.type, other.type, 'type');
}
- bool checkTypedef_typeParametersOfFunctionType(
- EquivalenceVisitor visitor, Typedef node, Typedef other) {
- return visitor.checkLists(
- node.typeParametersOfFunctionType,
- other.typeParametersOfFunctionType,
- visitor.checkDeclarations,
- 'typeParametersOfFunctionType');
- }
-
- bool checkTypedef_positionalParameters(
- EquivalenceVisitor visitor, Typedef node, Typedef other) {
- return visitor.checkLists(
- node.positionalParameters,
- other.positionalParameters,
- visitor.checkDeclarations,
- 'positionalParameters');
- }
-
- bool checkTypedef_namedParameters(
- EquivalenceVisitor visitor, Typedef node, Typedef other) {
- return visitor.checkLists(node.namedParameters, other.namedParameters,
- visitor.checkDeclarations, 'namedParameters');
- }
-
bool checkTypedef_reference(
EquivalenceVisitor visitor, Typedef node, Typedef other) {
return checkNamedNode_reference(visitor, node, other);
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 94b7cf2..46969af 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -853,9 +853,7 @@
}
}
- void writeFunctionType(FunctionType node,
- {List<VariableDeclaration>? typedefPositional,
- List<VariableDeclaration>? typedefNamed}) {
+ void writeFunctionType(FunctionType node) {
if (state == WORD) {
ensureSpace();
}
@@ -863,38 +861,14 @@
writeSymbol('(');
List<DartType> positional = node.positionalParameters;
- bool parametersAnnotated = false;
- if (typedefPositional != null) {
- for (VariableDeclaration formal in typedefPositional) {
- parametersAnnotated =
- parametersAnnotated || formal.annotations.length > 0;
- }
- }
- if (typedefNamed != null) {
- for (VariableDeclaration formal in typedefNamed) {
- parametersAnnotated =
- parametersAnnotated || formal.annotations.length > 0;
- }
- }
-
- if (parametersAnnotated && typedefPositional != null) {
- writeList(typedefPositional.take(node.requiredParameterCount),
- writeVariableDeclaration);
- } else {
- writeList(positional.take(node.requiredParameterCount), writeType);
- }
+ writeList(positional.take(node.requiredParameterCount), writeType);
if (node.requiredParameterCount < positional.length) {
if (node.requiredParameterCount > 0) {
writeComma();
}
writeSymbol('[');
- if (parametersAnnotated && typedefPositional != null) {
- writeList(typedefPositional.skip(node.requiredParameterCount),
- writeVariableDeclaration);
- } else {
- writeList(positional.skip(node.requiredParameterCount), writeType);
- }
+ writeList(positional.skip(node.requiredParameterCount), writeType);
writeSymbol(']');
}
if (node.namedParameters.isNotEmpty) {
@@ -902,11 +876,7 @@
writeComma();
}
writeSymbol('{');
- if (parametersAnnotated && typedefNamed != null) {
- writeList(typedefNamed, writeVariableDeclaration);
- } else {
- writeList(node.namedParameters, visitNamedType);
- }
+ writeList(node.namedParameters, visitNamedType);
writeSymbol('}');
}
writeSymbol(')');
@@ -1483,9 +1453,7 @@
writeSpaced('=');
DartType? type = node.type;
if (type is FunctionType) {
- writeFunctionType(type,
- typedefPositional: node.positionalParameters,
- typedefNamed: node.namedParameters);
+ writeFunctionType(type);
} else {
writeNode(type);
}
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index 858e978..d9f95f6 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -282,8 +282,7 @@
assert(state == null);
typedefState[node] = TypedefState.BeingChecked;
Set<TypeParameter> savedTypeParameters = typeParametersInScope;
- typeParametersInScope = node.typeParameters.toSet()
- ..addAll(node.typeParametersOfFunctionType);
+ typeParametersInScope = node.typeParameters.toSet();
TreeNode? savedParent = currentParent;
currentParent = node;
// Visit children without checking the parent pointer on the typedef itself
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 02cab5e..209910e 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -875,11 +875,6 @@
if (_usedTypedefs.add(typedef)) {
typedef.annotations = const <Expression>[];
_pass1.transformTypeParameterList(typedef.typeParameters, typedef);
- _pass1.transformTypeParameterList(
- typedef.typeParametersOfFunctionType, typedef);
- _pass1.transformVariableDeclarationList(
- typedef.positionalParameters, typedef);
- _pass1.transformVariableDeclarationList(typedef.namedParameters, typedef);
typedef.type?.accept(typeVisitor);
}
}
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index f30a460..3e6c317 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -203,11 +203,13 @@
"-ggdb3",
"-fno-rtti",
"-fno-exceptions",
- "-fno-strict-vtable-pointers", # Handle assignment updates vtable
- # pointers.
]
if (is_clang) {
- cflags += [ "-Wimplicit-fallthrough" ]
+ cflags += [
+ "-Wimplicit-fallthrough",
+ "-fno-strict-vtable-pointers", # Handle assignment updates vtable
+ # pointers.
+ ]
} else {
cflags += [ "-Wno-cast-function-type" ]
}
diff --git a/runtime/tests/vm/dart/flutter_regress_100441_test.dart b/runtime/tests/vm/dart/flutter_regress_100441_test.dart
new file mode 100644
index 0000000..d51cdff
--- /dev/null
+++ b/runtime/tests/vm/dart/flutter_regress_100441_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+StackTrace? stackTrace;
+
+main() async {
+ final values = [];
+ await for (final value in produce()) {
+ values.add(value);
+ }
+ Expect.equals('foo', values.single);
+ Expect.isNotNull(stackTrace!);
+}
+
+Stream<String> produce() async* {
+ await for (String response in produceInner()) {
+ yield response;
+ }
+}
+
+Stream<String> produceInner() async* {
+ yield 'foo';
+ stackTrace = StackTrace.current;
+}
diff --git a/runtime/tests/vm/dart_2/flutter_regress_100441_test.dart b/runtime/tests/vm/dart_2/flutter_regress_100441_test.dart
new file mode 100644
index 0000000..9250b6b
--- /dev/null
+++ b/runtime/tests/vm/dart_2/flutter_regress_100441_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, 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.
+
+// @dart=2.9
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+StackTrace stackTrace;
+
+main() async {
+ final values = [];
+ await for (final value in produce()) {
+ values.add(value);
+ }
+ Expect.equals('foo', values.single);
+ Expect.isNotNull(stackTrace);
+}
+
+Stream<String> produce() async* {
+ await for (String response in produceInner()) {
+ yield response;
+ }
+}
+
+Stream<String> produceInner() async* {
+ yield 'foo';
+ stackTrace = StackTrace.current;
+}
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index a6d5523..de73c0f 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -2180,7 +2180,7 @@
addl(dest, inc_imm);
}
-void Assembler::LoadDoubleConstant(XmmRegister dst, double value) {
+void Assembler::LoadDImmediate(XmmRegister dst, double value) {
// TODO(5410843): Need to have a code constants table.
int64_t constant = bit_cast<int64_t, double>(value);
pushl(Immediate(Utils::High32Bits(constant)));
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index f059fb2..8cee67d 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -713,6 +713,8 @@
}
}
+ void LoadDImmediate(XmmRegister dst, double value);
+
void Drop(intptr_t stack_elements);
void LoadIsolate(Register dst);
@@ -732,7 +734,6 @@
void PushObject(const Object& object);
void CompareObject(Register reg, const Object& object);
- void LoadDoubleConstant(XmmRegister dst, double value);
void LoadCompressed(Register dest, const Address& slot) { movl(dest, slot); }
diff --git a/runtime/vm/compiler/assembler/assembler_ia32_test.cc b/runtime/vm/compiler/assembler/assembler_ia32_test.cc
index 43c4818..e89073d 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32_test.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32_test.cc
@@ -4558,8 +4558,8 @@
"ret\n");
}
-ASSEMBLER_TEST_GENERATE(TestLoadDoubleConstant, assembler) {
- __ LoadDoubleConstant(XMM3, -12.34);
+ASSEMBLER_TEST_GENERATE(TestLoadDImmediate, assembler) {
+ __ LoadDImmediate(XMM3, -12.34);
__ pushl(EAX);
__ pushl(EAX);
__ movsd(Address(ESP, 0), XMM3);
@@ -4569,9 +4569,9 @@
__ ret();
}
-ASSEMBLER_TEST_RUN(TestLoadDoubleConstant, test) {
- typedef double (*TestLoadDoubleConstantCode)();
- double res = reinterpret_cast<TestLoadDoubleConstantCode>(test->entry())();
+ASSEMBLER_TEST_RUN(TestLoadDImmediate, test) {
+ typedef double (*TestLoadDImmediateCode)();
+ double res = reinterpret_cast<TestLoadDImmediateCode>(test->entry())();
EXPECT_FLOAT_EQ(-12.34, res, 0.0001);
EXPECT_DISASSEMBLY(
"push 0xc028ae14\n"
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 08da16e..3ec06a6 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -1796,9 +1796,6 @@
result_location = Location::FpuRegisterLocation(
AllocateFreeFpuRegister(blocked_fpu_registers));
break;
- case Location::kRequiresStackSlot:
- UNREACHABLE();
- break;
}
locs->set_out(0, result_location);
}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index f9e6f0e..30f6bec 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -6494,26 +6494,21 @@
LocationSummary* FfiCallInstr::MakeLocationSummaryInternal(
Zone* zone,
bool is_optimizing,
- const Register temp) const {
- // The temporary register needs to be callee-saved and not an argument
- // register.
- ASSERT(((1 << CallingConventions::kFfiAnyNonAbiRegister) &
- CallingConventions::kArgumentRegisters) == 0);
+ const RegList temps) const {
+ auto contains_call =
+ is_leaf_ ? LocationSummary::kNativeLeafCall : LocationSummary::kCall;
- // TODO(dartbug.com/45468): Investigate whether we can avoid spilling
- // registers across ffi leaf calls by not using `kCall` here.
LocationSummary* summary = new (zone) LocationSummary(
zone, /*num_inputs=*/InputCount(),
- /*num_temps=*/temp == kNoRegister ? 2 : 3, LocationSummary::kCall);
+ /*num_temps=*/Utils::CountOneBitsWord(temps), contains_call);
- const Register temp0 = CallingConventions::kFfiAnyNonAbiRegister;
- const Register temp1 = CallingConventions::kSecondNonArgumentRegister;
- ASSERT(temp0 != temp1);
- summary->set_temp(0, Location::RegisterLocation(temp0));
- summary->set_temp(1, Location::RegisterLocation(temp1));
-
- if (temp != kNoRegister) {
- summary->set_temp(2, Location::RegisterLocation(temp));
+ intptr_t reg_i = 0;
+ for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
+ if ((temps & (1 << reg)) != 0) {
+ summary->set_temp(reg_i,
+ Location::RegisterLocation(static_cast<Register>(reg)));
+ reg_i++;
+ }
}
summary->set_in(TargetAddressIndex(),
@@ -6524,10 +6519,7 @@
}
if (marshaller_.PassTypedData()) {
- // The register allocator already preserves this value across the call on
- // a stack slot, so we'll use the spilled value directly.
- summary->set_in(TypedDataIndex(), Location::RequiresStackSlot());
-
+ summary->set_in(TypedDataIndex(), Location::Any());
// We don't care about return location, but we need to pass a register.
summary->set_out(
0, Location::RegisterLocation(CallingConventions::kReturnReg));
@@ -6654,14 +6646,14 @@
void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler,
const Register temp0,
const Register temp1) {
- __ Comment("EmitReturnMoves");
-
const auto& returnLocation =
marshaller_.Location(compiler::ffi::kResultIndex);
if (returnLocation.payload_type().IsVoid()) {
return;
}
+ __ Comment("EmitReturnMoves");
+
NoTemporaryAllocator no_temp;
if (returnLocation.IsRegisters() || returnLocation.IsFpuRegisters()) {
const auto& src = returnLocation;
@@ -6675,15 +6667,20 @@
// Get the typed data pointer which we have pinned to a stack slot.
const Location typed_data_loc = locs()->in(TypedDataIndex());
- ASSERT(typed_data_loc.IsStackSlot());
- ASSERT(typed_data_loc.base_reg() == FPREG);
- // If this is a leaf call there is no extra call frame to step through.
- if (is_leaf_) {
- __ LoadMemoryValue(temp0, FPREG, typed_data_loc.ToStackSlotOffset());
+ if (typed_data_loc.IsStackSlot()) {
+ ASSERT(typed_data_loc.base_reg() == FPREG);
+ // If this is a leaf call there is no extra call frame to step through.
+ if (is_leaf_) {
+ __ LoadMemoryValue(temp0, FPREG, typed_data_loc.ToStackSlotOffset());
+ } else {
+ __ LoadMemoryValue(
+ temp0, FPREG,
+ kSavedCallerFpSlotFromFp * compiler::target::kWordSize);
+ __ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset());
+ }
} else {
- __ LoadMemoryValue(
- temp0, FPREG, kSavedCallerFpSlotFromFp * compiler::target::kWordSize);
- __ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset());
+ compiler->EmitMove(Location::RegisterLocation(temp0), typed_data_loc,
+ &no_temp);
}
__ LoadField(temp0,
compiler::FieldAddress(
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 65a9afb..a7dd7aa 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -5305,7 +5305,7 @@
LocationSummary* MakeLocationSummaryInternal(Zone* zone,
bool is_optimizing,
- const Register temp) const;
+ const RegList temps) const;
// Clobbers both given registers.
// `saved_fp` is used as the frame base to rebase off of.
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 411d58b..3dab29a 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -1402,27 +1402,34 @@
__ Drop(ArgumentCount()); // Drop the arguments.
}
+#define R(r) (1 << r)
+
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
- return MakeLocationSummaryInternal(zone, is_optimizing, R0);
+ return MakeLocationSummaryInternal(
+ zone, is_optimizing,
+ (R(R0) | R(CallingConventions::kFfiAnyNonAbiRegister) |
+ R(CallingConventions::kSecondNonArgumentRegister)));
}
+#undef R
+
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ const Register branch = locs()->in(TargetAddressIndex()).reg();
+
+ // The temps are indexed according to their register number.
+ const Register temp2 = locs()->temp(0).reg();
// For regular calls, this holds the FP for rebasing the original locations
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
- const Register saved_fp_or_sp = locs()->temp(0).reg();
- RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
- (1 << saved_fp_or_sp)) != 0);
- const Register temp1 = locs()->temp(1).reg();
- const Register temp2 = locs()->temp(2).reg();
- const Register branch = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_fp_or_sp = locs()->temp(1).reg();
+ const Register temp1 = locs()->temp(2).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
(1 << saved_fp_or_sp)) != 0);
- // temp doesn't need to be preserved.
+ // Other temps don't need to be preserved.
__ mov(saved_fp_or_sp,
is_leaf_ ? compiler::Operand(SPREG) : compiler::Operand(FPREG));
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 410cb0e..7ba8603 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1227,27 +1227,36 @@
__ Drop(ArgumentCount()); // Drop the arguments.
}
+#define R(r) (1 << r)
+
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
- return MakeLocationSummaryInternal(zone, is_optimizing, R11);
+ return MakeLocationSummaryInternal(
+ zone, is_optimizing,
+ (R(CallingConventions::kSecondNonArgumentRegister) | R(R11) |
+ R(CallingConventions::kFfiAnyNonAbiRegister) | R(R25)));
}
+#undef R
+
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ const Register branch = locs()->in(TargetAddressIndex()).reg();
+
+ // The temps are indexed according to their register number.
+ const Register temp1 = locs()->temp(0).reg();
+ const Register temp2 = locs()->temp(1).reg();
// For regular calls, this holds the FP for rebasing the original locations
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
- const Register saved_fp_or_sp = locs()->temp(0).reg();
- RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
- (1 << saved_fp_or_sp)) != 0);
- const Register temp1 = locs()->temp(1).reg();
- const Register temp2 = locs()->temp(2).reg();
- const Register branch = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_fp_or_sp = locs()->temp(2).reg();
+ const Register temp_csp = locs()->temp(3).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
(1 << saved_fp_or_sp)) != 0);
- // temps don't need to be preserved.
+ ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << temp_csp)) != 0);
+ // Other temps don't need to be preserved.
__ mov(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
@@ -1279,14 +1288,14 @@
// We are entering runtime code, so the C stack pointer must be restored
// from the stack limit to the top of the stack.
- __ mov(R25, CSP);
+ __ mov(temp_csp, CSP);
__ mov(CSP, SP);
__ blr(branch);
// Restore the Dart stack pointer.
__ mov(SP, CSP);
- __ mov(CSP, R25);
+ __ mov(CSP, temp_csp);
#if !defined(PRODUCT)
__ LoadImmediate(temp1, compiler::target::Thread::vm_tag_dart_id());
@@ -1315,14 +1324,14 @@
// We are entering runtime code, so the C stack pointer must be restored
// from the stack limit to the top of the stack.
- __ mov(R25, CSP);
+ __ mov(temp_csp, CSP);
__ mov(CSP, SP);
__ blr(branch);
// Restore the Dart stack pointer.
__ mov(SP, CSP);
- __ mov(CSP, R25);
+ __ mov(CSP, temp_csp);
// Update information in the thread object and leave the safepoint.
__ TransitionNativeToGenerated(temp1, /*leave_safepoint=*/true);
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index ce4c368..d608637 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -998,24 +998,33 @@
__ Drop(ArgumentCount()); // Drop the arguments.
}
+#define R(r) (1 << r)
+
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
- return MakeLocationSummaryInternal(zone, is_optimizing, kNoRegister);
+ return MakeLocationSummaryInternal(
+ zone, is_optimizing,
+ (R(CallingConventions::kSecondNonArgumentRegister) |
+ R(CallingConventions::kFfiAnyNonAbiRegister)));
}
+#undef R
+
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ const Register branch = locs()->in(TargetAddressIndex()).reg();
+
+ // The temps are indexed according to their register number.
+ const Register temp = locs()->temp(0).reg();
// For regular calls, this holds the FP for rebasing the original locations
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
- const Register saved_fp_or_sp = locs()->temp(0).reg();
- const Register temp = locs()->temp(1).reg();
- const Register branch = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_fp_or_sp = locs()->temp(1).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
(1 << saved_fp_or_sp)) != 0);
- // temp doesn't need to be preserved.
+ // Other temps don't need to be preserved.
__ movl(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index 9068abb..9f576c9 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -1374,10 +1374,14 @@
__ Drop(ArgumentCount() + 1); // Drop the arguments and result.
}
+#define R(r) (1 << r)
+
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
- LocationSummary* summary =
- MakeLocationSummaryInternal(zone, is_optimizing, CALLEE_SAVED_TEMP2);
+ LocationSummary* summary = MakeLocationSummaryInternal(
+ zone, is_optimizing,
+ (R(CallingConventions::kSecondNonArgumentRegister) |
+ R(CallingConventions::kFfiAnyNonAbiRegister) | R(CALLEE_SAVED_TEMP2)));
// A3/A4/A5 are blocked during Dart register allocation because they are
// assigned to TMP/TMP2/PP. This assignment is important for reducing code
// size. To work around this for FFI calls, the FFI argument definitions are
@@ -1400,17 +1404,20 @@
return summary;
}
+#undef R
+
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ const Register target = locs()->in(TargetAddressIndex()).reg();
+
+ // The temps are indexed according to their register number.
+ const Register temp1 = locs()->temp(0).reg();
// For regular calls, this holds the FP for rebasing the original locations
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
- const Register saved_fp_or_sp = locs()->temp(0).reg();
- RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
- (1 << saved_fp_or_sp)) != 0);
- const Register temp1 = locs()->temp(1).reg();
+ const Register saved_fp_or_sp = locs()->temp(1).reg();
const Register temp2 = locs()->temp(2).reg();
- const Register target = locs()->in(TargetAddressIndex()).reg();
+
ASSERT(temp1 != target);
ASSERT(temp2 != target);
ASSERT(temp1 != saved_fp_or_sp);
@@ -1420,7 +1427,7 @@
// Ensure these are callee-saved register and are preserved across the call.
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
(1 << saved_fp_or_sp)) != 0);
- // temps don't need to be preserved.
+ // Other temps don't need to be preserved.
__ mv(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index a03d0e86..95f8ac0 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -952,6 +952,227 @@
EXPECT_EQ(reinterpret_cast<intptr_t>(thread), result_int);
}
+// Helper to set up an inlined FfiCall by replacing a StaticCall.
+FlowGraph* SetupFfiFlowgraph(TestPipeline* pipeline,
+ Zone* zone,
+ const compiler::ffi::CallMarshaller& marshaller,
+ uword native_entry,
+ bool is_leaf) {
+ FlowGraph* flow_graph = pipeline->RunPasses({CompilerPass::kComputeSSA});
+
+ // Make an FfiCall based on ffi_trampoline that calls our native function.
+ auto ffi_call = new FfiCallInstr(zone, DeoptId::kNone, marshaller, is_leaf);
+ RELEASE_ASSERT(ffi_call->InputCount() == 1);
+ // TargetAddress is the function pointer called.
+ const Representation address_repr =
+ compiler::target::kWordSize == 4 ? kUnboxedUint32 : kUnboxedInt64;
+ ffi_call->SetInputAt(
+ ffi_call->TargetAddressIndex(),
+ new Value(flow_graph->GetConstant(
+ Integer::Handle(Integer::NewCanonical(native_entry)), address_repr)));
+
+ // Replace the placeholder StaticCall with an FfiCall to our native function.
+ {
+ StaticCallInstr* static_call = nullptr;
+ {
+ ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry(),
+ /*trace=*/false);
+ cursor.TryMatch({kMoveGlob, {kMatchStaticCall, &static_call}});
+ }
+ RELEASE_ASSERT(static_call != nullptr);
+
+ flow_graph->InsertBefore(static_call, ffi_call, /*env=*/nullptr,
+ FlowGraph::kEffect);
+ static_call->RemoveFromGraph(/*return_previous=*/false);
+ }
+
+ // Run remaining relevant compiler passes.
+ pipeline->RunAdditionalPasses({
+ CompilerPass::kApplyICData,
+ CompilerPass::kTryOptimizePatterns,
+ CompilerPass::kSetOuterInliningId,
+ CompilerPass::kTypePropagation,
+ // Skipping passes that don't seem to do anything for this test.
+ CompilerPass::kWidenSmiToInt32,
+ CompilerPass::kSelectRepresentations,
+ // Skipping passes that don't seem to do anything for this test.
+ CompilerPass::kTypePropagation,
+ CompilerPass::kRangeAnalysis,
+ // Skipping passes that don't seem to do anything for this test.
+ CompilerPass::kFinalizeGraph,
+ CompilerPass::kCanonicalize,
+ CompilerPass::kAllocateRegisters,
+ CompilerPass::kReorderBlocks,
+ });
+
+ return flow_graph;
+}
+
+// Test that FFI calls spill all live values to the stack, and that FFI leaf
+// calls are free to use available ABI callee-save registers to avoid spilling.
+// Additionally test that register allocation is done correctly by clobbering
+// all volatile registers in the native function being called.
+ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
+ SetFlagScope<int> sfs(&FLAG_sound_null_safety, kNullSafetyOptionStrong);
+
+ const char* kScript = R"(
+ import 'dart:ffi';
+
+ // This is purely a placeholder and is never called.
+ void placeholder() {}
+
+ // Will call the "doFfiCall" and exercise its code.
+ bool invokeDoFfiCall() {
+ final double result = doFfiCall(1, 2, 3, 1.0, 2.0, 3.0);
+ if (result != (2 + 3 + 4 + 2.0 + 3.0 + 4.0)) {
+ throw 'Failed. Result was $result.';
+ }
+ return true;
+ }
+
+ // Will perform a "C" call while having live values in registers
+ // across the FfiCall.
+ double doFfiCall(int a, int b, int c, double x, double y, double z) {
+ // Ensure there is at least one live value in a register.
+ a += 1;
+ b += 1;
+ c += 1;
+ x += 1.0;
+ y += 1.0;
+ z += 1.0;
+ // We'll replace this StaticCall with an FfiCall.
+ placeholder();
+ // Use the live value.
+ return (a + b + c + x + y + z);
+ }
+
+ // FFI trampoline function.
+ typedef NT = Void Function();
+ typedef DT = void Function();
+ Pointer<NativeFunction<NT>> ptr = Pointer.fromAddress(0);
+ DT getFfiTrampolineClosure() => ptr.asFunction(isLeaf:true);
+ )";
+
+ const auto& root_library = Library::Handle(LoadTestScript(kScript));
+
+ // Build a "C" function that we can actually invoke.
+ auto& c_function = Instructions::Handle(
+ BuildInstructions([](compiler::Assembler* assembler) {
+ // Clobber all volatile registers to make sure caller doesn't rely on
+ // any non-callee-save register.
+ for (intptr_t reg = 0; reg < kNumberOfFpuRegisters; reg++) {
+ if ((kAbiVolatileFpuRegs & (1 << reg)) != 0) {
+#if defined(TARGET_ARCH_ARM)
+ // On ARM we need an extra scratch register for LoadDImmediate.
+ assembler->LoadDImmediate(static_cast<DRegister>(reg), 0.0, R3);
+#else
+ assembler->LoadDImmediate(static_cast<FpuRegister>(reg), 0.0);
+#endif
+ }
+ }
+ for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
+ if ((kDartVolatileCpuRegs & (1 << reg)) != 0) {
+ assembler->LoadImmediate(static_cast<Register>(reg), 0xDEADBEEF);
+ }
+ }
+ assembler->Ret();
+ }));
+ uword native_entry = c_function.EntryPoint();
+
+ // Get initial compilation done.
+ Invoke(root_library, "invokeDoFfiCall");
+
+ const Function& do_ffi_call =
+ Function::Handle(GetFunction(root_library, "doFfiCall"));
+ RELEASE_ASSERT(!do_ffi_call.IsNull());
+
+ const auto& value = Closure::Handle(
+ Closure::RawCast(Invoke(root_library, "getFfiTrampolineClosure")));
+ RELEASE_ASSERT(value.IsClosure());
+ const auto& ffi_trampoline =
+ Function::ZoneHandle(Closure::Cast(value).function());
+ RELEASE_ASSERT(!ffi_trampoline.IsNull());
+
+ // Construct the FFICallInstr from the trampoline matching our native
+ // function.
+ const char* error = nullptr;
+ const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
+ thread->zone(), ffi_trampoline, &error);
+ RELEASE_ASSERT(error == nullptr);
+ RELEASE_ASSERT(marshaller_ptr != nullptr);
+ const auto& marshaller = *marshaller_ptr;
+
+ const auto& compile_and_run =
+ [&](bool is_leaf, std::function<void(ParallelMoveInstr*)> verify) {
+ // Build the SSA graph for "doFfiCall"
+ TestPipeline pipeline(do_ffi_call, CompilerPass::kJIT);
+ FlowGraph* flow_graph = SetupFfiFlowgraph(
+ &pipeline, thread->zone(), marshaller, native_entry, is_leaf);
+
+ {
+ ParallelMoveInstr* parallel_move = nullptr;
+ ILMatcher cursor(flow_graph,
+ flow_graph->graph_entry()->normal_entry(),
+ /*trace=*/false);
+ while (cursor.TryMatch(
+ {kMoveGlob, {kMatchAndMoveParallelMove, ¶llel_move}})) {
+ verify(parallel_move);
+ }
+ }
+
+ // Finish the compilation and attach code so we can run it.
+ pipeline.CompileGraphAndAttachFunction();
+
+ // Ensure we can successfully invoke the FFI call.
+ auto& result = Object::Handle(Invoke(root_library, "invokeDoFfiCall"));
+ RELEASE_ASSERT(result.IsBool());
+ EXPECT(Bool::Cast(result).value());
+ };
+
+ intptr_t num_cpu_reg_to_stack_nonleaf = 0;
+ intptr_t num_cpu_reg_to_stack_leaf = 0;
+ intptr_t num_fpu_reg_to_stack_nonleaf = 0;
+ intptr_t num_fpu_reg_to_stack_leaf = 0;
+
+ // Test non-leaf spills live values.
+ compile_and_run(/*is_leaf=*/false, [&](ParallelMoveInstr* parallel_move) {
+ // TargetAddress is passed in register, live values are all spilled.
+ for (int i = 0; i < parallel_move->NumMoves(); i++) {
+ auto move = parallel_move->moves()[i];
+ if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
+ num_cpu_reg_to_stack_nonleaf++;
+ } else if (move->src_slot()->IsFpuRegister() &&
+ move->dest_slot()->IsDoubleStackSlot()) {
+ num_fpu_reg_to_stack_nonleaf++;
+ }
+ }
+ });
+
+ // Test leaf calls do not cause spills of live values.
+ compile_and_run(/*is_leaf=*/true, [&](ParallelMoveInstr* parallel_move) {
+ // TargetAddress is passed in registers, live values are not spilled and
+ // remains in callee-save registers.
+ for (int i = 0; i < parallel_move->NumMoves(); i++) {
+ auto move = parallel_move->moves()[i];
+ if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
+ num_cpu_reg_to_stack_leaf++;
+ } else if (move->src_slot()->IsFpuRegister() &&
+ move->dest_slot()->IsDoubleStackSlot()) {
+ num_fpu_reg_to_stack_leaf++;
+ }
+ }
+ });
+
+ // We should have less moves to the stack (i.e. spilling) in leaf calls.
+ EXPECT_LT(num_cpu_reg_to_stack_leaf, num_cpu_reg_to_stack_nonleaf);
+ // We don't have volatile FPU registers on all platforms.
+ const bool has_callee_save_fpu_regs =
+ Utils::CountOneBitsWord(kAbiVolatileFpuRegs) <
+ Utils::CountOneBitsWord(kAllFpuRegistersList);
+ EXPECT(!has_callee_save_fpu_regs ||
+ num_fpu_reg_to_stack_leaf < num_fpu_reg_to_stack_nonleaf);
+}
+
static void TestConstantFoldToSmi(const Library& root_library,
const char* function_name,
CompilerPass::PipelineMode mode,
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 33b66ee..9a12a65 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1175,29 +1175,38 @@
__ Drop(ArgumentCount()); // Drop the arguments.
}
+#define R(r) (1 << r)
+
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
- // Use R10 as a temp register. We can't use RDI, RSI, RDX, R8, R9 as they are
- // arg registers, and R11 is TMP.
- return MakeLocationSummaryInternal(zone, is_optimizing, R10);
+ // Use R10 as a temp. register. We can't use RDI, RSI, RDX, R8, R9 as they are
+ // argument registers, and R11 is TMP.
+ return MakeLocationSummaryInternal(
+ zone, is_optimizing,
+ (R(CallingConventions::kSecondNonArgumentRegister) | R(R10) |
+ R(CallingConventions::kFfiAnyNonAbiRegister)));
}
+#undef R
+
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ const Register target_address = locs()->in(TargetAddressIndex()).reg();
+
+ // The temps are indexed according to their register number.
+ // For regular calls, this holds the FP for rebasing the original locations
+ // during EmitParamMoves.
+ const Register saved_fp = locs()->temp(0).reg();
+ const Register temp = locs()->temp(1).reg();
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
// Note: R12 doubles as CODE_REG, which gets clobbered during frame setup in
// regular calls.
- const Register saved_sp = locs()->temp(0).reg();
- // For regular calls, this holds the FP for rebasing the original locations
- // during EmitParamMoves.
- const Register saved_fp = locs()->temp(1).reg();
- const Register temp = locs()->temp(2).reg();
- const Register target_address = locs()->in(TargetAddressIndex()).reg();
+ const Register saved_sp = locs()->temp(2).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_sp)) != 0);
ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_fp)) != 0);
- // temp doesn't need to be preserved.
+ // Other temps don't need to be preserved.
if (is_leaf_) {
__ movq(saved_sp, SPREG);
diff --git a/runtime/vm/compiler/backend/linearscan.cc b/runtime/vm/compiler/backend/linearscan.cc
index 19191d1..266b485 100644
--- a/runtime/vm/compiler/backend/linearscan.cc
+++ b/runtime/vm/compiler/backend/linearscan.cc
@@ -465,6 +465,28 @@
}
}
+void FlowGraphAllocator::BlockCpuRegisters(intptr_t registers,
+ intptr_t from,
+ intptr_t to) {
+ for (intptr_t r = 0; r < kNumberOfCpuRegisters; r++) {
+ if ((registers & (1 << r)) != 0) {
+ BlockLocation(Location::RegisterLocation(static_cast<Register>(r)), from,
+ to);
+ }
+ }
+}
+
+void FlowGraphAllocator::BlockFpuRegisters(intptr_t fpu_registers,
+ intptr_t from,
+ intptr_t to) {
+ for (intptr_t r = 0; r < kNumberOfFpuRegisters; r++) {
+ if ((fpu_registers & (1 << r)) != 0) {
+ BlockLocation(Location::FpuRegisterLocation(static_cast<FpuRegister>(r)),
+ from, to);
+ }
+ }
+}
+
void LiveRange::Print() {
if (first_use_interval() == NULL) {
return;
@@ -1389,6 +1411,21 @@
}
}
+ // Block all volatile (i.e. not native ABI callee-save) registers.
+ if (locs->native_leaf_call()) {
+ BlockCpuRegisters(kDartVolatileCpuRegs, pos, pos + 1);
+ BlockFpuRegisters(kAbiVolatileFpuRegs, pos, pos + 1);
+#if defined(TARGET_ARCH_ARM)
+ // We do not yet have a way to say that we only want FPU registers that
+ // overlap S registers.
+ // Block all Q/D FPU registers above the 8/16 that have S registers in
+ // VFPv3-D32.
+ // This way we avoid ending up trying to do single-word operations on
+ // registers that don't support it.
+ BlockFpuRegisters(kFpuRegistersWithoutSOverlap, pos, pos + 1);
+#endif
+ }
+
// Block all allocatable registers for calls.
if (locs->always_calls() && !locs->callee_safe_call()) {
// Expected shape of live range:
@@ -1397,16 +1434,9 @@
// [--)
//
// The stack bitmap describes the position i.
- for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
- BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)), pos,
- pos + 1);
- }
+ BlockCpuRegisters(kAllCpuRegistersList, pos, pos + 1);
- for (intptr_t reg = 0; reg < kNumberOfFpuRegisters; reg++) {
- BlockLocation(
- Location::FpuRegisterLocation(static_cast<FpuRegister>(reg)), pos,
- pos + 1);
- }
+ BlockFpuRegisters(kAllFpuRegistersList, pos, pos + 1);
#if defined(DEBUG)
// Verify that temps, inputs and output were specified as fixed
@@ -1426,8 +1456,7 @@
pair->At(1).policy() == Location::kAny);
} else {
ASSERT(!locs->in(j).IsUnallocated() ||
- locs->in(j).policy() == Location::kAny ||
- locs->in(j).policy() == Location::kRequiresStackSlot);
+ locs->in(j).policy() == Location::kAny);
}
}
@@ -1441,7 +1470,7 @@
#endif
}
- if (locs->can_call()) {
+ if (locs->can_call() && !locs->native_leaf_call()) {
safepoints_.Add(current);
}
diff --git a/runtime/vm/compiler/backend/linearscan.h b/runtime/vm/compiler/backend/linearscan.h
index 2d6e919..bc26c63 100644
--- a/runtime/vm/compiler/backend/linearscan.h
+++ b/runtime/vm/compiler/backend/linearscan.h
@@ -156,6 +156,10 @@
bool* blocked_registers,
LiveRange** blocking_ranges);
+ void BlockCpuRegisters(intptr_t registers, intptr_t from, intptr_t to);
+
+ void BlockFpuRegisters(intptr_t fpu_registers, intptr_t from, intptr_t to);
+
intptr_t NumberOfRegisters() const { return number_of_registers_; }
// Find all safepoints that are covered by this live range.
diff --git a/runtime/vm/compiler/backend/locations.cc b/runtime/vm/compiler/backend/locations.cc
index 56640f8..9b5b166 100644
--- a/runtime/vm/compiler/backend/locations.cc
+++ b/runtime/vm/compiler/backend/locations.cc
@@ -185,8 +185,7 @@
// restrictions.
if (always_calls()) {
if (loc.IsUnallocated()) {
- ASSERT(loc.policy() == Location::kAny ||
- loc.policy() == Location::kRequiresStackSlot);
+ ASSERT(loc.policy() == Location::kAny);
} else if (loc.IsPairLocation()) {
ASSERT(!loc.AsPairLocation()->At(0).IsUnallocated() ||
loc.AsPairLocation()->At(0).policy() == Location::kAny);
@@ -311,8 +310,6 @@
return "R";
case kRequiresFpuRegister:
return "DR";
- case kRequiresStackSlot:
- return "RS";
case kWritableRegister:
return "WR";
case kSameAsFirstInput:
diff --git a/runtime/vm/compiler/backend/locations.h b/runtime/vm/compiler/backend/locations.h
index b9988948..754852e 100644
--- a/runtime/vm/compiler/backend/locations.h
+++ b/runtime/vm/compiler/backend/locations.h
@@ -257,7 +257,6 @@
kPrefersRegister,
kRequiresRegister,
kRequiresFpuRegister,
- kRequiresStackSlot,
kWritableRegister,
kSameAsFirstInput,
};
@@ -285,10 +284,6 @@
return UnallocatedLocation(kRequiresFpuRegister);
}
- static Location RequiresStackSlot() {
- return UnallocatedLocation(kRequiresStackSlot);
- }
-
static Location WritableRegister() {
return UnallocatedLocation(kWritableRegister);
}
@@ -719,12 +714,19 @@
class LocationSummary : public ZoneAllocated {
public:
enum ContainsCall {
- kNoCall, // Used registers must be reserved as tmp.
- kCall, // Registers have been saved and can be used without reservation.
- kCallCalleeSafe, // Registers will be saved by the callee.
- kCallOnSlowPath, // Used registers must be reserved as tmp.
- kCallOnSharedSlowPath // Registers used to invoke shared stub must be
- // reserved as tmp.
+ // Used registers must be reserved as tmp.
+ kNoCall,
+ // Registers have been saved and can be used without reservation.
+ kCall,
+ // Registers will be saved by the callee.
+ kCallCalleeSafe,
+ // Used registers must be reserved as tmp.
+ kCallOnSlowPath,
+ // Registers used to invoke shared stub must be reserved as tmp.
+ kCallOnSharedSlowPath,
+ // Location is a native leaf call so any register not in the native ABI
+ // callee-save (or input/output/tmp) set might get clobbered.
+ kNativeLeafCall
};
LocationSummary(Zone* zone,
@@ -800,6 +802,8 @@
return contains_call_ == kCallOnSharedSlowPath;
}
+ bool native_leaf_call() const { return contains_call_ == kNativeLeafCall; }
+
void PrintTo(BaseTextBuffer* f) const;
static LocationSummary* Make(Zone* zone,
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 15f48ef..a94761c 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2832,19 +2832,6 @@
SkipStringReference(); // Read part URI index.
}
-void KernelReaderHelper::SkipLibraryTypedef() {
- SkipCanonicalNameReference(); // read canonical name.
- ReadUInt(); // read source_uri_index.
- ReadPosition(); // read position.
- SkipStringReference(); // read name index.
- SkipListOfExpressions(); // read annotations.
- SkipTypeParametersList(); // read type parameters.
- SkipDartType(); // read type.
- SkipTypeParametersList(); // read type parameters of function type.
- SkipListOfVariableDeclarations(); // read positional parameters.
- SkipListOfVariableDeclarations(); // read named parameters.
-}
-
TokenPosition KernelReaderHelper::ReadPosition() {
TokenPosition position = reader_.ReadPosition();
RecordTokenPosition(position);
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 315e468..4016697 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -1279,7 +1279,6 @@
void SkipLibraryCombinator();
void SkipLibraryDependency();
void SkipLibraryPart();
- void SkipLibraryTypedef();
TokenPosition ReadPosition();
Tag ReadTag(uint8_t* payload = NULL);
uint8_t ReadFlags() { return reader_.ReadFlags(); }
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 7b43812..7406b47 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -30,6 +30,8 @@
// to compare LR register code.
#define LR LR_DO_NOT_USE_DIRECTLY
+#define R(reg) (static_cast<RegList>(1) << (reg))
+
// We support both VFPv3-D16 and VFPv3-D32 profiles, but currently only one at
// a time.
#if defined(__ARM_ARCH_7A__)
@@ -209,6 +211,9 @@
D31 = 31,
kNumberOfDRegisters = 32,
#endif
+ // Number of D registers that overlap S registers.
+ // One D register overlaps two S registers, so regardless of the numbers of D
+ // registers, there are only 32 S registers that are overlapped.
kNumberOfOverlappingDRegisters = 16,
};
@@ -243,6 +248,10 @@
Q15 = 15,
kNumberOfQRegisters = 16,
#endif
+ // Number of Q registers that overlap S registers.
+ // One Q register overlaps four S registers, so regardless of the numbers of Q
+ // registers, there are only 32 S registers that are overlapped.
+ kNumberOfOverlappingQRegisters = 8,
};
static inline DRegister EvenDRegisterOf(QRegister q) {
@@ -256,7 +265,7 @@
static inline SRegister EvenSRegisterOf(DRegister d) {
#if defined(VFPv3_D32)
// When we have 32 D registers, the S registers only overlap the first 16.
- // That is, there are only 32 S registers.
+ // That is, there are only ever 32 S registers in any extension.
ASSERT(d < D16);
#endif
return static_cast<SRegister>(d * 2);
@@ -535,6 +544,7 @@
// List of registers used in load/store multiple.
typedef uint16_t RegList;
const RegList kAllCpuRegistersList = 0xFFFF;
+const RegList kAllFpuRegistersList = (1 << kNumberOfFpuRegisters) - 1;
// C++ ABI call registers.
const RegList kAbiArgumentCpuRegs =
@@ -572,7 +582,11 @@
const int kDartVolatileCpuRegCount = 5;
#endif
-#define R(reg) (static_cast<RegList>(1) << (reg))
+const RegList kAbiVolatileFpuRegs = R(Q0) | R(Q1) | R(Q2) | R(Q3);
+
+const RegList kFpuRegistersWithoutSOverlap =
+ kAllFpuRegistersList &
+ ~((1 << QRegister::kNumberOfOverlappingQRegisters) - 1);
class CallingConventions {
public:
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index a7f00d9..5be6e98 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -81,7 +81,9 @@
};
enum VRegister {
+ // v0 Volatile; Parameter/scratch register, result register.
V0 = 0,
+ // v1-v7 Volatile; Parameter/scratch register.
V1 = 1,
V2 = 2,
V3 = 3,
@@ -89,6 +91,8 @@
V5 = 5,
V6 = 6,
V7 = 7,
+ // v8-v15 Non-volatile; Scratch registers
+ // Only the bottom 64 bits are non-volatile! [ARM IHI 0055B, 5.1.2]
V8 = 8,
V9 = 9,
V10 = 10,
@@ -97,6 +101,7 @@
V13 = 13,
V14 = 14,
V15 = 15,
+ // v16-v31 Volatile; Scratch registers.
V16 = 16,
V17 = 17,
V18 = 18,
@@ -104,7 +109,7 @@
V20 = 20,
V21 = 21,
V22 = 22,
- V23 = 24,
+ V23 = 23,
V24 = 24,
V25 = 25,
V26 = 26,
@@ -430,6 +435,11 @@
const int kDartVolatileCpuRegCount = 15;
const int kDartVolatileFpuRegCount = 24;
+const RegList kAbiVolatileFpuRegs =
+ R(V0) | R(V1) | R(V2) | R(V3) | R(V4) | R(V5) | R(V6) | R(V7) | R(V16) |
+ R(V17) | R(V18) | R(V19) | R(V20) | R(V21) | R(V22) | R(V23) | R(V24) |
+ R(V25) | R(V26) | R(V27) | R(V28) | R(V29) | R(V30) | R(V31);
+
constexpr int kStoreBufferWrapperSize = 32;
class CallingConventions {
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index 061771d..733dd8c 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -16,6 +16,8 @@
namespace dart {
+#define R(reg) (1 << (reg))
+
enum Register {
EAX = 0,
ECX = 1,
@@ -29,6 +31,8 @@
kNoRegister = -1, // Signals an illegal register.
};
+// Low and high bytes registers of the the first four general purpose registers.
+// The other four general purpose registers do not have byte registers.
enum ByteRegister {
AL = 0,
CL = 1,
@@ -42,6 +46,10 @@
};
inline ByteRegister ByteRegisterOf(Register reg) {
+ // This only works for EAX, ECX, EDX, EBX.
+ // Remaining Register values map to high byte of the above registers.
+ RELEASE_ASSERT(reg == Register::EAX || reg == Register::ECX ||
+ reg == Register::EDX || reg == Register::EBX);
return static_cast<ByteRegister>(reg);
}
@@ -263,12 +271,23 @@
typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFF;
+const RegList kAllFpuRegistersList = (1 << kNumberOfFpuRegisters) - 1;
const intptr_t kReservedCpuRegisters = (1 << SPREG) | (1 << FPREG) | (1 << THR);
// CPU registers available to Dart allocator.
const RegList kDartAvailableCpuRegs =
kAllCpuRegistersList & ~kReservedCpuRegisters;
+const RegList kAbiPreservedCpuRegs = (1 << EDI) | (1 << ESI) | (1 << EBX);
+
+// Registers available to Dart that are not preserved by runtime calls.
+const RegList kDartVolatileCpuRegs =
+ kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
+
+const RegList kAbiVolatileFpuRegs = kAllFpuRegistersList;
+
+#undef R
+
enum ScaleFactor {
TIMES_1 = 0,
TIMES_2 = 1,
@@ -332,8 +351,7 @@
static const intptr_t kXmmArgumentRegisters = 0;
static const intptr_t kNumFpuArgRegs = 0;
- static constexpr intptr_t kCalleeSaveCpuRegisters =
- (1 << EDI) | (1 << ESI) | (1 << EBX);
+ static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs;
static const bool kArgumentIntRegXorFpuReg = false;
diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h
index 30a720f..f8430df 100644
--- a/runtime/vm/constants_riscv.h
+++ b/runtime/vm/constants_riscv.h
@@ -387,6 +387,7 @@
typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFFFFFFFF;
+const RegList kAllFpuRegistersList = 0xFFFFFFFF;
#define R(reg) (static_cast<RegList>(1) << (reg))
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index b8bc0e7..930f588 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -16,6 +16,8 @@
namespace dart {
+#define R(reg) (static_cast<RegList>(1) << (reg))
+
enum Register {
RAX = 0,
RCX = 1,
@@ -345,7 +347,7 @@
const RegList kAllFpuRegistersList = 0xFFFF;
const RegList kReservedCpuRegisters =
- (1 << SPREG) | (1 << FPREG) | (1 << TMP) | (1 << PP) | (1 << THR);
+ R(SPREG) | R(FPREG) | R(TMP) | R(PP) | R(THR);
constexpr intptr_t kNumberOfReservedCpuRegisters = 5;
// CPU registers available to Dart allocator.
const RegList kDartAvailableCpuRegs =
@@ -354,6 +356,20 @@
kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters;
constexpr int kStoreBufferWrapperSize = 13;
+#if defined(DART_TARGET_OS_WINDOWS)
+const RegList kAbiPreservedCpuRegs =
+ R(RBX) | R(RSI) | R(RDI) | R(R12) | R(R13) | R(R14) | R(R15);
+const RegList kAbiVolatileFpuRegs =
+ R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3) | R(XMM4) | R(XMM5);
+#else
+const RegList kAbiPreservedCpuRegs = R(RBX) | R(R12) | R(R13) | R(R14) | R(R15);
+const RegList kAbiVolatileFpuRegs = kAllFpuRegistersList;
+#endif
+
+// Registers available to Dart that are not preserved by runtime calls.
+const RegList kDartVolatileCpuRegs =
+ kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
+
enum ScaleFactor {
TIMES_1 = 0,
TIMES_2 = 1,
@@ -383,8 +399,6 @@
TIMES_COMPRESSED_HALF_WORD_SIZE = TIMES_COMPRESSED_WORD_SIZE - 1,
};
-#define R(reg) (static_cast<RegList>(1) << (reg))
-
class CallingConventions {
public:
#if defined(DART_TARGET_OS_WINDOWS)
@@ -422,11 +436,7 @@
static const intptr_t kVolatileCpuRegisters =
R(RAX) | R(RCX) | R(RDX) | R(R8) | R(R9) | R(R10) | R(R11);
- static const intptr_t kVolatileXmmRegisters =
- R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3) | R(XMM4) | R(XMM5);
-
- static const intptr_t kCalleeSaveCpuRegisters =
- R(RBX) | R(RSI) | R(RDI) | R(R12) | R(R13) | R(R14) | R(R15);
+ static const RegList kVolatileXmmRegisters = kAbiVolatileFpuRegs;
static const intptr_t kCalleeSaveXmmRegisters =
R(XMM6) | R(XMM7) | R(XMM8) | R(XMM9) | R(XMM10) | R(XMM11) | R(XMM12) |
@@ -490,13 +500,7 @@
R(RSI) | R(RDI) | R(R8) |
R(R9) | R(R10) | R(R11);
- static const intptr_t kVolatileXmmRegisters =
- R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3) | R(XMM4) | R(XMM5) | R(XMM6) |
- R(XMM7) | R(XMM8) | R(XMM9) | R(XMM10) | R(XMM11) | R(XMM12) | R(XMM13) |
- R(XMM14) | R(XMM15);
-
- static const intptr_t kCalleeSaveCpuRegisters =
- R(RBX) | R(R12) | R(R13) | R(R14) | R(R15);
+ static const RegList kVolatileXmmRegisters = kAbiVolatileFpuRegs;
static const intptr_t kCalleeSaveXmmRegisters = 0;
@@ -530,6 +534,8 @@
#endif
+ static const intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs;
+
COMPILE_ASSERT((kArgumentRegisters & kReservedCpuRegisters) == 0);
static constexpr Register kFfiAnyNonAbiRegister = R12;
@@ -544,9 +550,6 @@
(kArgumentRegisters | R(kPointerToReturnStructRegisterCall))) == 0);
};
-constexpr intptr_t kAbiPreservedCpuRegs =
- CallingConventions::kCalleeSaveCpuRegisters;
-
#undef R
class Instr {
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index ff0cfa2..0efa27f 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
// Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 76;
-static const uint32_t kMaxSupportedKernelFormatVersion = 76;
+static const uint32_t kMinSupportedKernelFormatVersion = 77;
+static const uint32_t kMaxSupportedKernelFormatVersion = 77;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 9b665c2..4c7cbcf 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -83,7 +83,8 @@
var_data_field(Field::Handle(zone)),
state_field(Field::Handle(zone)),
on_data_field(Field::Handle(zone)),
- state_data_field(Field::Handle(zone)) {
+ state_data_field(Field::Handle(zone)),
+ has_value_field(Field::Handle(zone)) {
const auto& async_lib = Library::Handle(zone, Library::AsyncLibrary());
// Look up classes:
// - async:
@@ -143,6 +144,9 @@
state_data_field =
stream_iterator_class.LookupFieldAllowPrivate(Symbols::_stateData());
ASSERT(!state_data_field.IsNull());
+ has_value_field =
+ stream_iterator_class.LookupFieldAllowPrivate(Symbols::_hasValue());
+ ASSERT(!has_value_field.IsNull());
}
ClosurePtr CallerClosureFinder::GetCallerInFutureImpl(const Object& future) {
@@ -201,9 +205,33 @@
// If the async* stream is await-for'd:
if (callback_instance_.GetClassId() == stream_iterator_class.id()) {
- // _StreamIterator._stateData
- future_ = Instance::Cast(callback_instance_).GetField(state_data_field);
- return GetCallerInFutureImpl(future_);
+ // If `_hasValue` is true then the `StreamIterator._stateData` field
+ // contains the iterator's value. In that case we cannot unwind anymore.
+ //
+ // Notice: With correct async* semantics this may never be true: The async*
+ // generator should only be invoked to produce a vaue if there's an
+ // in-progress `await streamIterator.moveNext()` call. Once such call has
+ // finished the async* generator should be paused/yielded until the next
+ // such call - and being paused/yielded means it should not appear in stack
+ // traces.
+ //
+ // See dartbug.com/48695.
+ const auto& stream_iterator = Instance::Cast(callback_instance_);
+ if (stream_iterator.GetField(has_value_field) ==
+ Object::bool_true().ptr()) {
+ return Closure::null();
+ }
+
+ // If we have an await'er for `await streamIterator.moveNext()` we continue
+ // unwinding there.
+ //
+ // Notice: With correct async* semantics this may always contain a Future
+ // See also comment above as well as dartbug.com/48695.
+ future_ = stream_iterator.GetField(state_data_field);
+ if (future_.GetClassId() == future_impl_class.id()) {
+ return GetCallerInFutureImpl(future_);
+ }
+ return Closure::null();
}
UNREACHABLE(); // If no onData is found we have a bug.
diff --git a/runtime/vm/stack_trace.h b/runtime/vm/stack_trace.h
index 566a0a8..0683cf7 100644
--- a/runtime/vm/stack_trace.h
+++ b/runtime/vm/stack_trace.h
@@ -92,6 +92,7 @@
Field& state_field;
Field& on_data_field;
Field& state_data_field;
+ Field& has_value_field;
DISALLOW_COPY_AND_ASSIGN(CallerClosureFinder);
};
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index ba78ad7..52ec048 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -414,6 +414,7 @@
V(_handleMessage, "_handleMessage") \
V(_handleFinalizerMessage, "_handleFinalizerMessage") \
V(_handleNativeFinalizerMessage, "_handleNativeFinalizerMessage") \
+ V(_hasValue, "_hasValue") \
V(_instanceOf, "_instanceOf") \
V(_listGetAt, "_listGetAt") \
V(_listLength, "_listLength") \
diff --git a/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart b/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart
new file mode 100644
index 0000000..f4e7512
--- /dev/null
+++ b/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, 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.
+
+library dart.js_util_wasm;
+
+import "dart:_internal";
+import "dart:js_util_wasm";
+import "dart:wasm";
+
+/// js_util_wasm methods used by the wasm runtime.
+@pragma("wasm:export", "\$listLength")
+double _listLength(List list) => list.length.toDouble();
+
+@pragma("wasm:export", "\$listRead")
+WasmAnyRef? _listRead(List<Object?> list, double index) =>
+ jsifyRaw(list[index.toInt()]);
+
+@pragma("wasm:export", "\$listAllocate")
+List<Object?> _listAllocate() => [];
+
+@pragma("wasm:export", "\$listAdd")
+void _listAdd(List<Object?> list, WasmAnyRef? item) =>
+ list.add(dartifyRaw(item));
+
+@patch
+Object _jsObjectToDartObject(WasmAnyRef ref) => unsafeCastOpaque<Object>(ref);
+
+@patch
+WasmAnyRef _jsObjectFromDartObject(Object object) =>
+ unsafeCastOpaque<WasmAnyRef>(object);
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index 3de0d58..42b6082 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -978,6 +978,7 @@
/// This will usually cause the [_subscription] to be paused, but as an
/// optimization, we only pause after the [moveNext] future has been
/// completed.
+ @pragma("vm:entry-point")
bool _hasValue = false;
_StreamIterator(final Stream<T> stream)
diff --git a/sdk/lib/js_util/js_util_sources.gni b/sdk/lib/js_util/js_util_sources.gni
index d3ac6d2..011df2e 100644
--- a/sdk/lib/js_util/js_util_sources.gni
+++ b/sdk/lib/js_util/js_util_sources.gni
@@ -2,4 +2,7 @@
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
-js_util_sdk_sources = [ "js_util.dart" ]
+js_util_sdk_sources = [
+ "js_util.dart",
+ "js_util_wasm.dart",
+]
diff --git a/sdk/lib/js_util/js_util_wasm.dart b/sdk/lib/js_util/js_util_wasm.dart
new file mode 100644
index 0000000..49b4a90
--- /dev/null
+++ b/sdk/lib/js_util/js_util_wasm.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2022, 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.
+
+// Prototype js util library for wasm.
+library dart.js_util_wasm;
+
+import 'dart:wasm';
+
+/// [JSValue] is the root of the JS interop object hierarchy.
+class JSValue {
+ final WasmAnyRef _ref;
+
+ JSValue(this._ref);
+
+ static JSValue? box(WasmAnyRef? ref) => ref == null ? null : JSValue(ref);
+
+ WasmAnyRef toAnyRef() => _ref;
+ String toString() => _jsStringToDartString(_ref);
+ List<Object?> toObjectList() => _jsArrayToDartList(_ref);
+ Object toObject() => _jsObjectToDartObject(_ref);
+}
+
+/// Raw private JS functions.
+external WasmAnyRef _jsObjectFromDartObject(Object object);
+
+external Object _jsObjectToDartObject(WasmAnyRef ref);
+
+@pragma("wasm:import", "dart2wasm.arrayFromDartList")
+external WasmAnyRef _jsArrayFromDartList(List<Object?> list);
+
+@pragma("wasm:import", "dart2wasm.arrayToDartList")
+external List<Object?> _jsArrayToDartList(WasmAnyRef list);
+
+@pragma("wasm:import", "dart2wasm.stringFromDartString")
+external WasmAnyRef _jsStringFromDartString(String string);
+
+@pragma("wasm:import", "dart2wasm.stringToDartString")
+external String _jsStringToDartString(WasmAnyRef string);
+
+/// Raw public JS functions.
+/// These are public temporarily to give performance conscious users an escape
+/// hatch while we decide what this API will actually look like. They may
+/// become private in the future, or disappear entirely. For descriptions of the
+/// API, please see the corresponding non-raw functions.
+@pragma("wasm:import", "dart2wasm.eval")
+external void evalRaw(WasmAnyRef code);
+
+@pragma("wasm:import", "dart2wasm.dartify")
+external WasmAnyRef? dartifyRaw(WasmAnyRef? object);
+
+@pragma("wasm:import", "dart2wasm.newObject")
+external WasmAnyRef newObjectRaw();
+
+@pragma("wasm:import", "dart2wasm.globalThis")
+external WasmAnyRef globalThisRaw();
+
+@pragma("wasm:import", "dart2wasm.callConstructorVarArgs")
+external WasmAnyRef callConstructorVarArgsRaw(
+ WasmAnyRef o, WasmAnyRef name, WasmAnyRef args);
+
+@pragma("wasm:import", "dart2wasm.hasProperty")
+external bool hasPropertyRaw(WasmAnyRef o, WasmAnyRef name);
+
+@pragma("wasm:import", "dart2wasm.getProperty")
+external WasmAnyRef? getPropertyRaw(WasmAnyRef o, WasmAnyRef name);
+
+@pragma("wasm:import", "dart2wasm.setProperty")
+external WasmAnyRef? setPropertyRaw(
+ WasmAnyRef o, WasmAnyRef name, WasmAnyRef? value);
+
+@pragma("wasm:import", "dart2wasm.callMethodVarArgs")
+external WasmAnyRef? callMethodVarArgsRaw(
+ WasmAnyRef o, WasmAnyRef method, WasmAnyRef? args);
+
+WasmAnyRef? jsifyRaw(Object? object) {
+ if (object == null) {
+ return null;
+ } else if (object is JSValue) {
+ return object.toAnyRef();
+ } else if (object is String) {
+ return _jsStringFromDartString(object);
+ } else if (object is List<Object?>) {
+ return _jsArrayFromDartList(object);
+ } else {
+ return _jsObjectFromDartObject(object);
+ }
+}
+
+/// Conversion functions.
+/// TODO(joshualitt): Only a small set of types currently work:
+/// JS -> Dart:
+/// null
+/// strings
+/// arrays
+/// opaque Dart objects passed to JS
+/// Dart -> JS:
+/// null
+/// boolean
+/// doubles
+/// strings
+/// lists
+/// opaque JS objects passed to Dart
+/// In the future we would like to support more types, at least maps,
+/// and to fix some of the issues returning some types from JS.
+
+/// Extension methods for conversions.
+extension StringToJS on String {
+ JSValue toJS() => JSValue(_jsStringFromDartString(this));
+}
+
+extension ListOfObjectToJS on List<Object?> {
+ JSValue toJS() => JSValue(_jsArrayFromDartList(this));
+}
+
+extension ObjectToJS on Object {
+ JSValue toJS() => JSValue(_jsObjectFromDartObject(this));
+}
+
+/// Recursively converts objects from Dart to JS.
+JSValue? jsify(Object? object) => JSValue.box(jsifyRaw(object));
+
+/// Recursively converts objects from JS to Dart.
+Object? dartify(JSValue? object) => object == null
+ ? null
+ : _jsObjectToDartObject(dartifyRaw(object.toAnyRef())!);
+
+/// js util methods.
+/// These are low level calls into JS, and require care to use correctly.
+
+/// Evals a snippet of JS code in a Dart string.
+void eval(String code) => evalRaw(code.toJS().toAnyRef());
+
+/// Creates a new JS object literal and returns it.
+JSValue newObject() => JSValue(newObjectRaw());
+
+/// Returns a reference to `globalThis`.
+JSValue globalThis() => JSValue(globalThisRaw());
+
+/// Gets a [String] name property off of a JS object [o], invokes it as
+/// a constructor with a JS array of arguments [args], and returns the
+/// constructed JS object.
+JSValue callConstructorVarArgs(JSValue o, String name, List<JSValue?> args) =>
+ JSValue(callConstructorVarArgsRaw(
+ o.toAnyRef(), name.toJS().toAnyRef(), args.toJS().toAnyRef()));
+
+/// Checks for a [String] name on a JS object [o].
+bool hasProperty(JSValue o, String name) =>
+ hasPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef());
+
+/// Gets a JS property with [String] name off of a JS object [o].
+JSValue? getProperty(JSValue o, String name) =>
+ JSValue.box(getPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef()));
+
+/// Sets a JS property with [String] name on JS object [o] to the JS value
+/// [value], then returns [value].
+JSValue? setProperty(JSValue o, String name, JSValue? value) => JSValue.box(
+ setPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef(), value?.toAnyRef()));
+
+/// Calls a JS method with a [String] name on JS object [o] with a JS array
+/// of arguments [args] and returns the resulting JS value.
+JSValue? callMethodVarArgs(JSValue o, String method, List<JSValue?> args) =>
+ JSValue.box(callMethodVarArgsRaw(
+ o.toAnyRef(), method.toJS().toAnyRef(), args.toJS().toAnyRef()));
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index dae53f7..212955e 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -232,6 +232,10 @@
"isolate": {
"uri": "isolate/isolate.dart"
},
+ "js_util_wasm": {
+ "uri": "js_util/js_util_wasm.dart",
+ "patches": "_internal/wasm/lib/js_util_wasm_patch.dart"
+ },
"math": {
"uri": "math/math.dart",
"patches": "_internal/wasm/lib/math_patch.dart"
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index ca10300..e0a8e42 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -216,6 +216,9 @@
uri: "html/dartium/nativewrappers.dart"
isolate:
uri: isolate/isolate.dart
+ js_util_wasm:
+ uri: js_util/js_util_wasm.dart
+ patches: _internal/wasm/lib/js_util_wasm_patch.dart
math:
uri: math/math.dart
patches: _internal/wasm/lib/math_patch.dart
diff --git a/tests/web/wasm/js_util_test.dart b/tests/web/wasm/js_util_test.dart
new file mode 100644
index 0000000..a5b31c1
--- /dev/null
+++ b/tests/web/wasm/js_util_test.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:js_util_wasm';
+
+import 'package:expect/expect.dart';
+
+void createObjectTest() {
+ JSValue o = newObject();
+ Expect.isFalse(hasProperty(o, 'foo'));
+ Expect.equals('bar', setProperty(o, 'foo', 'bar'.toJS()).toString());
+ Expect.isTrue(hasProperty(o, 'foo'));
+ Expect.equals('bar', getProperty(o, 'foo').toString());
+}
+
+// Unfortunately, lists do not currently compare identically.
+void _expectListEquals(List<Object?> l, List<Object?> r) {
+ Expect.equals(l.length, r.length);
+ for (int i = 0; i < l.length; i++) {
+ Expect.equals(l[i], r[i]);
+ }
+}
+
+void evalAndConstructTest() {
+ eval(r'''
+ function JSClass(c) {
+ this.c = c;
+ this.sum = (a, b) => {
+ return a + b + this.c;
+ }
+ this.list = ['a', 'b', 'c'];
+ }
+ globalThis.JSClass = JSClass;
+ ''');
+ JSValue gt = globalThis();
+ JSValue jsClass = callConstructorVarArgs(gt, 'JSClass', ['world!'.toJS()]);
+ Expect.equals(
+ 'hello world!',
+ callMethodVarArgs(jsClass, 'sum', ['hello'.toJS(), ' '.toJS()])
+ .toString());
+ _expectListEquals(
+ ['a', 'b', 'c'], getProperty(jsClass, 'list')!.toObjectList());
+}
+
+class Foo {
+ final int i;
+ Foo(this.i);
+}
+
+void dartObjectRoundTripTest() {
+ JSValue o = newObject();
+ setProperty(o, 'foo', Foo(4).toJS());
+ Object foo = getProperty(o, 'foo')!.toObject();
+ Expect.equals(4, (foo as Foo).i);
+}
+
+void deepConversionsTest() {
+ // Dart to JS.
+ Expect.isNull(dartify(jsify(null)));
+ Expect.equals(true, dartify(jsify(true)));
+ Expect.equals(2.0, dartify(jsify(2.0)));
+ Expect.equals('foo', dartify(jsify('foo')));
+ _expectListEquals(
+ ['a', 'b', 'c'], dartify(jsify(['a', 'b', 'c'])) as List<Object?>);
+
+ // JS to Dart.
+ eval(r'''
+ globalThis.a = null;
+ globalThis.b = 'foo';
+ globalThis.c = ['a', 'b', 'c'];
+ ''');
+ JSValue gt = globalThis();
+ Expect.isNull(dartify(getProperty(gt, 'a')));
+ Expect.equals('foo', dartify(getProperty(gt, 'b')));
+ _expectListEquals(
+ ['a', 'b', 'c'], dartify(getProperty(gt, 'c')) as List<Object?>);
+}
+
+void main() {
+ createObjectTest();
+ evalAndConstructTest();
+ dartObjectRoundTripTest();
+ deepConversionsTest();
+}
diff --git a/tests/web/web.status b/tests/web/web.status
index c54cd80..d20e136 100644
--- a/tests/web/web.status
+++ b/tests/web/web.status
@@ -5,6 +5,9 @@
[ $compiler != dart2js ]
dummy_compiler_test: SkipByDesign # Issue 30773. Test should be migrated as a unit test of dart2js, is only intended to test self-hosting.
+[ $compiler != dart2wasm ]
+wasm/*: SkipByDesign
+
[ $runtime == jsshell ]
deferred/load_in_correct_order_test: SkipByDesign # jsshell preamble does not support this test.
diff --git a/tools/VERSION b/tools/VERSION
index 3bced45..7b9e5d4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 255
+PRERELEASE 256
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index cf5a954..4f67da1 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2969,7 +2969,8 @@
"arguments": [
"-ndart2wasm-hostasserts-linux-x64-d8",
"language",
- "corelib"
+ "corelib",
+ "web/wasm"
],
"shards": 30,
"fileset": "dart2wasm_hostasserts"
diff --git a/tools/manage_deps.dart b/tools/manage_deps.dart
index f954327..80b1b56 100755
--- a/tools/manage_deps.dart
+++ b/tools/manage_deps.dart
@@ -231,6 +231,7 @@
cmd[0],
cmd.skip(1).toList(),
workingDirectory: workingDirectory,
+ environment: {'DEPOT_TOOLS_UPDATE': '0'},
);
printSuccessTrailer(result, onFailure);
final output = (result.stdout as String);