[fasta] Invalidate part file when init from dill
This CL adds a test that invalidates a part file after initializing from
a dill file. It futher more fixes the found issue so the part file (and
subsequent entire library it is a part of) is invalidated. Previously
invalidating a part file had no effect.
Change-Id: I0e76998d0ce2247b58c4a0e6cd691062b3069a2b
Reviewed-on: https://dart-review.googlesource.com/45181
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 6c6e50a..c530048 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -240,7 +240,8 @@
List<Uri> invalidatedImportUris = <Uri>[];
// Compute [builders] and [invalidatedImportUris].
- addBuilderAndInvalidateUris(Uri uri, LibraryBuilder library) {
+ addBuilderAndInvalidateUris(Uri uri, LibraryBuilder library,
+ [bool recursive = true]) {
builders[uri] = library;
if (invalidatedFileUris.contains(uri) ||
(uri != library.fileUri &&
@@ -250,9 +251,14 @@
invalidatedFileUris.contains(library.library.fileUri))) {
invalidatedImportUris.add(uri);
}
+ if (!recursive) return;
if (library is SourceLibraryBuilder) {
for (var part in library.parts) {
- addBuilderAndInvalidateUris(part.uri, part);
+ addBuilderAndInvalidateUris(part.uri, part, false);
+ }
+ } else if (library is DillLibraryBuilder) {
+ for (var part in library.library.parts) {
+ addBuilderAndInvalidateUris(part.fileUri, library, false);
}
}
}
@@ -276,11 +282,19 @@
// Remove all dependencies of [invalidatedImportUris] from builders.
List<Uri> workList = invalidatedImportUris;
while (workList.isNotEmpty) {
- LibraryBuilder current = builders.remove(workList.removeLast());
+ Uri removed = workList.removeLast();
+ LibraryBuilder current = builders.remove(removed);
// [current] is null if the corresponding key (URI) has already been
// removed.
if (current != null) {
Set<Uri> s = directDependencies[current.uri];
+ if (current.uri != removed) {
+ if (s == null) {
+ s = directDependencies[removed];
+ } else {
+ s.addAll(directDependencies[removed]);
+ }
+ }
if (s != null) {
// [s] is null for leaves.
for (Uri dependency in s) {
@@ -290,7 +304,16 @@
}
}
- return builders.values.where((builder) => !builder.isPart).toList();
+ // Builders contain mappings from part uri to builder, meaning the same
+ // builder can exist multiple times in the values list.
+ Set<Uri> seenUris = new Set<Uri>();
+ List<LibraryBuilder> result = <LibraryBuilder>[];
+ for (var builder in builders.values) {
+ if (builder.isPart) continue;
+ if (!seenUris.add(builder.fileUri)) continue;
+ result.add(builder);
+ }
+ return result;
}
@override
diff --git a/pkg/front_end/test/incremental_load_from_dill_test.dart b/pkg/front_end/test/incremental_load_from_dill_test.dart
index 94d1b0f..686cda7 100644
--- a/pkg/front_end/test/incremental_load_from_dill_test.dart
+++ b/pkg/front_end/test/incremental_load_from_dill_test.dart
@@ -37,6 +37,7 @@
"testStrongModeMixins2_a.dart: Error: "
"The parameter 'value' of the method 'A::child' has type");
await runPassingTest(testInvalidateExportOfMain);
+ await runPassingTest(testInvalidatePart);
}
void runFailingTest(dynamic test, String expectContains) async {
@@ -63,6 +64,54 @@
}
}
+/// Invalidate a part file.
+void testInvalidatePart() async {
+ final Uri a = outDir.uri.resolve("testInvalidatePart_a.dart");
+ final Uri b = outDir.uri.resolve("testInvalidatePart_b.dart");
+ final Uri c = outDir.uri.resolve("testInvalidatePart_c.dart");
+ final Uri d = outDir.uri.resolve("testInvalidatePart_d.dart");
+
+ Uri output = outDir.uri.resolve("testInvalidatePart_full.dill");
+ Uri bootstrappedOutput =
+ outDir.uri.resolve("testInvalidatePart_full_from_bootstrap.dill");
+
+ new File.fromUri(a).writeAsStringSync("""
+ library a;
+ import 'testInvalidatePart_c.dart';
+ part 'testInvalidatePart_b.dart';
+ """);
+ new File.fromUri(b).writeAsStringSync("""
+ part of a;
+ b() { print("b"); }
+ """);
+ new File.fromUri(c).writeAsStringSync("""
+ library c;
+ part 'testInvalidatePart_d.dart';
+ """);
+ new File.fromUri(d).writeAsStringSync("""
+ part of c;
+ d() { print("d"); }
+ """);
+
+ Stopwatch stopwatch = new Stopwatch()..start();
+ await normalCompile(a, output, options: getOptions(true));
+ print("Normal compile took ${stopwatch.elapsedMilliseconds} ms");
+
+ stopwatch.reset();
+ bool bootstrapResult = await bootstrapCompile(
+ a, bootstrappedOutput, output, [b],
+ performSizeTests: true, options: getOptions(true));
+ print("Bootstrapped compile(s) from ${output.pathSegments.last} "
+ "took ${stopwatch.elapsedMilliseconds} ms");
+ Expect.isTrue(bootstrapResult);
+
+ // Compare the two files.
+ List<int> normalDillData = new File.fromUri(output).readAsBytesSync();
+ List<int> bootstrappedDillData =
+ new File.fromUri(bootstrappedOutput).readAsBytesSync();
+ checkBootstrappedIsEqual(normalDillData, bootstrappedDillData);
+}
+
/// Invalidate the entrypoint which just exports another file (which has main).
void testInvalidateExportOfMain() async {
final Uri a = outDir.uri.resolve("testInvalidateExportOfMain_a.dart");
@@ -86,7 +135,7 @@
stopwatch.reset();
bool bootstrapResult = await bootstrapCompile(
a, bootstrappedOutput, output, [a],
- performSizeTests: false, options: getOptions(true));
+ performSizeTests: true, options: getOptions(true));
print("Bootstrapped compile(s) from ${output.pathSegments.last} "
"took ${stopwatch.elapsedMilliseconds} ms");
Expect.isTrue(bootstrapResult);
@@ -129,7 +178,7 @@
stopwatch.reset();
bool bootstrapResult = await bootstrapCompile(
a, bootstrappedOutput, output, [a],
- performSizeTests: false, options: getOptions(true));
+ performSizeTests: true, options: getOptions(true));
print("Bootstrapped compile(s) from ${output.pathSegments.last} "
"took ${stopwatch.elapsedMilliseconds} ms");
Expect.isTrue(bootstrapResult);
@@ -168,7 +217,7 @@
stopwatch.reset();
bool bootstrapResult = await bootstrapCompile(
a, bootstrappedOutput, output, [a],
- performSizeTests: false, options: getOptions(true));
+ performSizeTests: true, options: getOptions(true));
print("Bootstrapped compile(s) from ${output.pathSegments.last} "
"took ${stopwatch.elapsedMilliseconds} ms");
Expect.isTrue(bootstrapResult);