Implement GC for FileState(s).

Also fix for invoking watch().

R=ahe@google.com, paulberry@google.com, sigmund@google.com
BUG=

Review-Url: https://codereview.chromium.org/2929363002 .
This commit is contained in:
Konstantin Shcheglov 2017-06-12 08:47:59 -07:00
parent 10a5bfaa08
commit 5e8ceb60ab
3 changed files with 90 additions and 1 deletions

View file

@ -54,6 +54,10 @@ class FileState {
Set<FileState> _directReferencedFiles = new Set<FileState>();
List<FileState> _directReferencedLibraries = <FileState>[];
/// This flag is set to `true` during the mark phase of garbage collection
/// and set back to `false` for survived instances.
bool _gcMarked = false;
FileState._(this._fsState, this.uri, this.fileUri);
/// The MD5 signature of the file API as a byte array.
@ -309,6 +313,35 @@ class FileSystemState {
/// The `file:` URI of all files currently tracked by this instance.
Iterable<Uri> get fileUris => _fileUriToFile.keys;
/// Perform mark and sweep garbage collection of [FileState]s.
void gc(Uri entryPoint) {
void mark(FileState file) {
if (!file._gcMarked) {
file._gcMarked = true;
file._directReferencedFiles.forEach(mark);
}
}
var file = _uriToFile[entryPoint];
if (file == null) return;
mark(file);
var urisToRemove = new Set<Uri>();
var fileUrisToRemove = new Set<Uri>();
for (var file in _uriToFile.values) {
if (file._gcMarked) {
file._gcMarked = false;
} else {
urisToRemove.add(file.uri);
fileUrisToRemove.add(file.fileUri);
}
}
urisToRemove.forEach(_uriToFile.remove);
fileUrisToRemove.forEach(_fileUriToFile.remove);
}
/// Return the [FileState] for the given [absoluteUri], or `null` if the
/// [absoluteUri] cannot be resolved into a file URI.
///

View file

@ -79,8 +79,16 @@ class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
: _logger = _options.logger,
_byteStore = _options.byteStore {
_computeSalt();
Future<Null> onFileAdded(Uri uri) {
if (watch != null) {
return watch(uri, true);
}
return new Future.value();
}
_fsState = new FileSystemState(
_options.fileSystem, _uriTranslator, _salt, (uri) => watch(uri, true));
_options.fileSystem, _uriTranslator, _salt, onFileAdded);
}
@override
@ -304,6 +312,9 @@ class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
await file.refresh();
}
}
// The file graph might have changed, perform GC.
_fsState.gc(_entryPoint);
});
}

View file

@ -141,6 +141,51 @@ baz() => 44;
''');
}
test_gc() async {
var a = writeFile('/a.dart', '');
var b = writeFile('/b.dart', '');
var c = writeFile('/c.dart', 'import "a.dart";');
var d = writeFile('/d.dart', 'import "b.dart";');
var e = writeFile(
'/e.dart',
r'''
import "c.dart";
import "d.dart";
''');
var eFile = await fsState.getFile(e);
// The root and four files.
expect(fsState.fileUris, contains(e));
expect(fsState.fileUris, contains(a));
expect(fsState.fileUris, contains(b));
expect(fsState.fileUris, contains(c));
expect(fsState.fileUris, contains(d));
// No changes after GC.
fsState.gc(e);
expect(fsState.fileUris, contains(e));
expect(fsState.fileUris, contains(a));
expect(fsState.fileUris, contains(b));
expect(fsState.fileUris, contains(c));
expect(fsState.fileUris, contains(d));
// Update e.dart so that it does not reference c.dart anymore.
// Then GC removes both c.dart and a.dart it references.
writeFile(
'/e.dart',
r'''
import "d.dart";
''');
await eFile.refresh();
fsState.gc(e);
expect(fsState.fileUris, contains(e));
expect(fsState.fileUris, isNot(contains(a)));
expect(fsState.fileUris, contains(b));
expect(fsState.fileUris, isNot(contains(c)));
expect(fsState.fileUris, contains(d));
}
test_getFile() async {
var a = writeFile('/a.dart', '');
var b = writeFile('/b.dart', '');