Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1323)

Unified Diff: pkg/front_end/lib/src/incremental/file_state.dart

Issue 2871783002: Start using FileState/FileSystemState to provide consistent view. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/front_end/lib/src/incremental/file_state.dart
diff --git a/pkg/front_end/lib/src/incremental/file_state.dart b/pkg/front_end/lib/src/incremental/file_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..556fad12383af9ece0423c0133d7a501bae5d40f
--- /dev/null
+++ b/pkg/front_end/lib/src/incremental/file_state.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2017, 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 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:front_end/file_system.dart';
+import 'package:front_end/src/fasta/parser/top_level_parser.dart';
+import 'package:front_end/src/fasta/scanner.dart';
+import 'package:front_end/src/fasta/source/directive_listener.dart';
+import 'package:front_end/src/fasta/translate_uri.dart';
+
+/// Information about a file being analyzed, explicitly or implicitly.
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 nit: now that this includes compilation maybe "ana
scheglov 2017/05/09 00:16:28 I will changed it to "compiled".
+///
+/// It provides a consistent view on its properties.
+///
+/// The properties are not guaranteed to represent the most recent state
+/// of the file system. To update the file to the most recent state, [refresh]
+/// should be called.
+class FileState {
+ final FileSystemState _fsState;
+
+ /// The resolved URI of the file in the file system.
+ final Uri fileUri;
+
+ bool _exists;
+ List<int> _contentBytes;
+ String _content;
Paul Berry 2017/05/08 22:10:48 Is it necessary to keep both _contentBytes and _co
scheglov 2017/05/09 00:16:28 I think you're right. I will fix this in the next
+
+ List<FileState> _importedFiles;
+ List<FileState> _exportedFiles;
+ List<FileState> _partedFiles;
Siggi Cherem (dart-lang) 2017/05/08 23:55:18 nit: rename to _partfiles
scheglov 2017/05/09 00:16:28 Done.
+
+ Set<FileState> _directReferencedFiles = new Set<FileState>();
+
+ FileState._(this._fsState, this.fileUri);
+
+ /// The content of the file.
+ String get content => _content;
+
+ /// The content bytes of the file.
+ List<int> get contentBytes => _contentBytes;
+
+ /// Whether the file exists.
+ bool get exists => _exists;
+
+ @override
+ int get hashCode => fileUri.hashCode;
+
+ /// Return the set of transitive files - the file itself and all of the
+ /// directly or indirectly referenced files.
+ Set<FileState> get transitiveFiles {
+ // TODO(scheglov) add caching.
+ var transitiveFiles = new Set<FileState>();
+
+ void appendReferenced(FileState file) {
+ if (transitiveFiles.add(file)) {
+ file._directReferencedFiles.forEach(appendReferenced);
+ }
+ }
+
+ appendReferenced(this);
+ return transitiveFiles;
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return other is FileState && other.fileUri == fileUri;
+ }
+
+ /// Read the file content and ensure that all of the file properties are
+ /// consistent with the read content, including all its dependencies.
+ Future<Null> refresh() async {
+ // Read the content.
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 I expect `refresh` will only get called on files t
scheglov 2017/05/09 00:16:28 Correct. We read each file at least once when ref
+ try {
+ FileSystemEntity entry = _fsState.fileSystem.entityForUri(fileUri);
+ _contentBytes = await entry.readAsBytes();
+ _content = UTF8.decode(_contentBytes);
+ _exists = true;
+ } catch (_) {
+ _contentBytes = new Uint8List(0);
+ _content = '';
+ _exists = false;
+ }
+
+ // Parse directives.
+ ScannerResult scannerResults = scanString(_content);
+ var listener = new DirectiveListener();
+ new TopLevelParser(listener).parseUnit(scannerResults.tokens);
+
+ // Build the graph.
+ _importedFiles = <FileState>[];
+ _exportedFiles = <FileState>[];
+ _partedFiles = <FileState>[];
+ for (String import in listener.imports) {
Paul Berry 2017/05/08 22:10:49 Can we rename "import" to "importedFile" or someth
Siggi Cherem (dart-lang) 2017/05/08 23:55:18 nit: I'd use `var` instead of `String` For the va
scheglov 2017/05/09 00:16:28 Done.
scheglov 2017/05/09 00:16:28 I don't see the type of "listener.imports". It is
Siggi Cherem (dart-lang) 2017/05/09 00:26:15 Not sure why that's not working: listener.imports
scheglov 2017/05/09 00:29:43 Ah, sorry, I should have been more explicit. The t
+ await _addFileForRelativeUri(_importedFiles, import);
+ }
+ for (String export in listener.exports) {
Paul Berry 2017/05/08 22:10:49 Similar issue here
scheglov 2017/05/09 00:16:28 Done.
+ await _addFileForRelativeUri(_exportedFiles, export);
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 optional - it appears to me that it is safe to par
scheglov 2017/05/09 00:16:28 That's an interesting idea. I will keep it in mind
+ }
+ for (String export in listener.parts) {
Paul Berry 2017/05/08 22:10:49 Here we should rename "export" to something like "
scheglov 2017/05/09 00:16:28 Done.
+ await _addFileForRelativeUri(_partedFiles, export);
+ }
+ // TODO(scheglov) make this optional
Paul Berry 2017/05/08 22:10:49 Why? All files import 'dart:core' (either implici
scheglov 2017/05/09 00:16:28 Well, yeah, I thought about adding it only if dart
+ await _addFileForRelativeUri(_importedFiles, 'dart:core');
+
+ // Compute referenced files.
+ _directReferencedFiles = new Set<FileState>()
+ ..addAll(_importedFiles)
+ ..addAll(_exportedFiles)
+ ..addAll(_partedFiles);
+ }
+
+ /// Add the [FileState] for the given [relativeUri] to the [files].
+ /// Do nothing if the URI cannot be parsed, cannot correspond any file, etc.
+ Future<Null> _addFileForRelativeUri(
+ List<FileState> files, String relativeUri) async {
+ if (relativeUri.isEmpty) {
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 optional nit: I tend to prefer no braces on these
scheglov 2017/05/09 00:16:28 Done.
+ return;
+ }
+
+ // Resolve the relative URI into absolute.
+ // The result is either:
+ // 1) The absolute file URI.
+ // 2) The absolute non-file URI, e.g. `package:foo/foo.dart`.
+ Uri absoluteUri;
+ try {
+ absoluteUri = this.fileUri.resolve(relativeUri);
+ } on FormatException {
+ return;
+ }
+
+ // Resolve the absolute URI into the absolute file URI.
+ Uri fileUri = _fsState.uriTranslator.translate(absoluteUri);
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 note that this shadows FielState.fileUri - it migh
scheglov 2017/05/09 00:16:28 Done.
+ if (fileUri == null) {
Siggi Cherem (dart-lang) 2017/05/08 23:55:17 same here about one-line if ("if (fileUri == null)
scheglov 2017/05/09 00:16:28 Done.
+ return;
+ }
+
+ FileState file = await _fsState.getFile(fileUri);
+ files.add(file);
+ }
+}
+
+/// Information about known file system state.
+class FileSystemState {
+ final FileSystem fileSystem;
+ final TranslateUri uriTranslator;
+
+ _FileSystemView _fileSystemView;
+
+ /// Mapping from file URIs to corresponding [FileState]s.
+ final Map<Uri, FileState> _fileUriToFile = {};
+
+ FileSystemState(this.fileSystem, this.uriTranslator);
+
+ /// Return the [FileSystem] that is backed by this [FileSystemState]. The
+ /// files in this [FileSystem] always have the same content as the
+ /// corresponding [FileState]s, thus avoiding race conditions when a file
+ /// is updated on the actual file system.
+ FileSystem get fileSystemView {
+ return _fileSystemView ??= new _FileSystemView(this);
+ }
+
+ /// Return the [FileState] for the given resolved file [fileUri].
+ /// The returned file has the last known state since it was last refreshed.
+ Future<FileState> getFile(Uri fileUri) async {
+ FileState file = _fileUriToFile[fileUri];
+ if (file == null) {
+ file = new FileState._(this, fileUri);
+ _fileUriToFile[fileUri] = file;
+
+ // Build the sub-graph of the file.
+ await file.refresh();
+ }
+ return file;
+ }
+}
+
+/// [FileSystemState] based implementation of [FileSystem].
+/// It provides a consistent view on the known file system state.
+class _FileSystemView implements FileSystem {
+ final FileSystemState fsState;
+
+ _FileSystemView(this.fsState);
+
+ @override
+ FileSystemEntity entityForUri(Uri uri) {
+ FileState file = fsState._fileUriToFile[uri];
+ return new _FileSystemViewEntry(uri, file);
+ }
+}
+
+/// [FileSystemState] based implementation of [FileSystemEntity].
+class _FileSystemViewEntry implements FileSystemEntity {
+ @override
+ final Uri uri;
+
+ final FileState file;
+
+ _FileSystemViewEntry(this.uri, this.file);
+
+ @override
+ Future<bool> exists() async => file?.exists ?? false;
+
+ @override
+ Future<DateTime> lastModified() async {
+ throw new UnimplementedError();
Paul Berry 2017/05/08 22:10:49 Maybe throw a StateError() here with a message say
scheglov 2017/05/09 00:16:28 Done. I agree, this API might not always be access
+ }
+
+ @override
+ Future<List<int>> readAsBytes() async {
+ _throwIfDoesNotExist();
+ return file.contentBytes;
+ }
+
+ @override
+ Future<String> readAsString() async {
+ _throwIfDoesNotExist();
+ return file.content;
+ }
+
+ void _throwIfDoesNotExist() {
+ if (file == null) {
+ throw new FileSystemException(uri, 'File $uri does not exist.');
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698