OLD | NEW |
| (Empty) |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'dart:async'; | |
6 import 'dart:convert'; | |
7 import 'dart:typed_data'; | |
8 | |
9 import 'package:front_end/file_system.dart'; | |
10 import 'package:front_end/src/fasta/parser/top_level_parser.dart'; | |
11 import 'package:front_end/src/fasta/scanner.dart'; | |
12 import 'package:front_end/src/fasta/source/directive_listener.dart'; | |
13 import 'package:front_end/src/fasta/translate_uri.dart'; | |
14 | |
15 /// Information about a file being compiled, explicitly or implicitly. | |
16 /// | |
17 /// It provides a consistent view on its properties. | |
18 /// | |
19 /// The properties are not guaranteed to represent the most recent state | |
20 /// of the file system. To update the file to the most recent state, [refresh] | |
21 /// should be called. | |
22 class FileState { | |
23 final FileSystemState _fsState; | |
24 | |
25 /// The resolved URI of the file in the file system. | |
26 final Uri fileUri; | |
27 | |
28 bool _exists; | |
29 List<int> _contentBytes; | |
30 String _content; | |
31 | |
32 List<FileState> _importedFiles; | |
33 List<FileState> _exportedFiles; | |
34 List<FileState> _partFiles; | |
35 | |
36 Set<FileState> _directReferencedFiles = new Set<FileState>(); | |
37 | |
38 FileState._(this._fsState, this.fileUri); | |
39 | |
40 /// The content of the file. | |
41 String get content => _content; | |
42 | |
43 /// The content bytes of the file. | |
44 List<int> get contentBytes => _contentBytes; | |
45 | |
46 /// Whether the file exists. | |
47 bool get exists => _exists; | |
48 | |
49 @override | |
50 int get hashCode => fileUri.hashCode; | |
51 | |
52 /// Return the set of transitive files - the file itself and all of the | |
53 /// directly or indirectly referenced files. | |
54 Set<FileState> get transitiveFiles { | |
55 // TODO(scheglov) add caching. | |
56 var transitiveFiles = new Set<FileState>(); | |
57 | |
58 void appendReferenced(FileState file) { | |
59 if (transitiveFiles.add(file)) { | |
60 file._directReferencedFiles.forEach(appendReferenced); | |
61 } | |
62 } | |
63 | |
64 appendReferenced(this); | |
65 return transitiveFiles; | |
66 } | |
67 | |
68 @override | |
69 bool operator ==(Object other) { | |
70 return other is FileState && other.fileUri == fileUri; | |
71 } | |
72 | |
73 /// Read the file content and ensure that all of the file properties are | |
74 /// consistent with the read content, including all its dependencies. | |
75 Future<Null> refresh() async { | |
76 // Read the content. | |
77 try { | |
78 FileSystemEntity entry = _fsState.fileSystem.entityForUri(fileUri); | |
79 _contentBytes = await entry.readAsBytes(); | |
80 _content = UTF8.decode(_contentBytes); | |
81 _exists = true; | |
82 } catch (_) { | |
83 _contentBytes = new Uint8List(0); | |
84 _content = ''; | |
85 _exists = false; | |
86 } | |
87 | |
88 // Parse directives. | |
89 ScannerResult scannerResults = scanString(_content); | |
90 var listener = new DirectiveListener(); | |
91 new TopLevelParser(listener).parseUnit(scannerResults.tokens); | |
92 | |
93 // Build the graph. | |
94 _importedFiles = <FileState>[]; | |
95 _exportedFiles = <FileState>[]; | |
96 _partFiles = <FileState>[]; | |
97 await _addFileForRelativeUri(_importedFiles, 'dart:core'); | |
98 for (String uri in listener.imports) { | |
99 await _addFileForRelativeUri(_importedFiles, uri); | |
100 } | |
101 for (String uri in listener.exports) { | |
102 await _addFileForRelativeUri(_exportedFiles, uri); | |
103 } | |
104 for (String uri in listener.parts) { | |
105 await _addFileForRelativeUri(_partFiles, uri); | |
106 } | |
107 | |
108 // Compute referenced files. | |
109 _directReferencedFiles = new Set<FileState>() | |
110 ..addAll(_importedFiles) | |
111 ..addAll(_exportedFiles) | |
112 ..addAll(_partFiles); | |
113 } | |
114 | |
115 /// Add the [FileState] for the given [relativeUri] to the [files]. | |
116 /// Do nothing if the URI cannot be parsed, cannot correspond any file, etc. | |
117 Future<Null> _addFileForRelativeUri( | |
118 List<FileState> files, String relativeUri) async { | |
119 if (relativeUri.isEmpty) return; | |
120 | |
121 // Resolve the relative URI into absolute. | |
122 // The result is either: | |
123 // 1) The absolute file URI. | |
124 // 2) The absolute non-file URI, e.g. `package:foo/foo.dart`. | |
125 Uri absoluteUri; | |
126 try { | |
127 absoluteUri = fileUri.resolve(relativeUri); | |
128 } on FormatException { | |
129 return; | |
130 } | |
131 | |
132 // Resolve the absolute URI into the absolute file URI. | |
133 Uri resolvedUri = _fsState.uriTranslator.translate(absoluteUri); | |
134 if (resolvedUri == null) return; | |
135 | |
136 FileState file = await _fsState.getFile(resolvedUri); | |
137 files.add(file); | |
138 } | |
139 } | |
140 | |
141 /// Information about known file system state. | |
142 class FileSystemState { | |
143 final FileSystem fileSystem; | |
144 final TranslateUri uriTranslator; | |
145 | |
146 _FileSystemView _fileSystemView; | |
147 | |
148 /// Mapping from file URIs to corresponding [FileState]s. | |
149 final Map<Uri, FileState> _fileUriToFile = {}; | |
150 | |
151 FileSystemState(this.fileSystem, this.uriTranslator); | |
152 | |
153 /// Return the [FileSystem] that is backed by this [FileSystemState]. The | |
154 /// files in this [FileSystem] always have the same content as the | |
155 /// corresponding [FileState]s, thus avoiding race conditions when a file | |
156 /// is updated on the actual file system. | |
157 FileSystem get fileSystemView { | |
158 return _fileSystemView ??= new _FileSystemView(this); | |
159 } | |
160 | |
161 /// Return the [FileState] for the given resolved file [fileUri]. | |
162 /// The returned file has the last known state since it was last refreshed. | |
163 Future<FileState> getFile(Uri fileUri) async { | |
164 FileState file = _fileUriToFile[fileUri]; | |
165 if (file == null) { | |
166 file = new FileState._(this, fileUri); | |
167 _fileUriToFile[fileUri] = file; | |
168 | |
169 // Build the sub-graph of the file. | |
170 await file.refresh(); | |
171 } | |
172 return file; | |
173 } | |
174 } | |
175 | |
176 /// [FileSystemState] based implementation of [FileSystem]. | |
177 /// It provides a consistent view on the known file system state. | |
178 class _FileSystemView implements FileSystem { | |
179 final FileSystemState fsState; | |
180 | |
181 _FileSystemView(this.fsState); | |
182 | |
183 @override | |
184 FileSystemEntity entityForUri(Uri uri) { | |
185 FileState file = fsState._fileUriToFile[uri]; | |
186 return new _FileSystemViewEntry(uri, file); | |
187 } | |
188 } | |
189 | |
190 /// [FileSystemState] based implementation of [FileSystemEntity]. | |
191 class _FileSystemViewEntry implements FileSystemEntity { | |
192 @override | |
193 final Uri uri; | |
194 | |
195 final FileState file; | |
196 | |
197 _FileSystemViewEntry(this.uri, this.file); | |
198 | |
199 @override | |
200 Future<bool> exists() async => file?.exists ?? false; | |
201 | |
202 @override | |
203 Future<DateTime> lastModified() async { | |
204 throw new StateError( | |
205 'FileSystemViewEntry modification stamp should not be queried'); | |
206 } | |
207 | |
208 @override | |
209 Future<List<int>> readAsBytes() async { | |
210 _throwIfDoesNotExist(); | |
211 return file.contentBytes; | |
212 } | |
213 | |
214 @override | |
215 Future<String> readAsString() async { | |
216 _throwIfDoesNotExist(); | |
217 return file.content; | |
218 } | |
219 | |
220 void _throwIfDoesNotExist() { | |
221 if (file == null) { | |
222 throw new FileSystemException(uri, 'File $uri does not exist.'); | |
223 } | |
224 } | |
225 } | |
OLD | NEW |