OLD | NEW |
---|---|
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:typed_data'; | 6 import 'dart:typed_data'; |
7 | 7 |
8 import 'package:crypto/crypto.dart'; | 8 import 'package:crypto/crypto.dart'; |
9 import 'package:front_end/file_system.dart'; | 9 import 'package:front_end/file_system.dart'; |
10 import 'package:front_end/src/base/api_signature.dart'; | |
10 import 'package:front_end/src/base/resolve_relative_uri.dart'; | 11 import 'package:front_end/src/base/resolve_relative_uri.dart'; |
11 import 'package:front_end/src/dependency_walker.dart' as graph; | 12 import 'package:front_end/src/dependency_walker.dart' as graph; |
12 import 'package:front_end/src/fasta/parser/dart_vm_native.dart'; | 13 import 'package:front_end/src/fasta/parser/dart_vm_native.dart'; |
14 import 'package:front_end/src/fasta/parser/listener.dart' show Listener; | |
15 import 'package:front_end/src/fasta/parser/parser.dart' show Parser, optional; | |
13 import 'package:front_end/src/fasta/parser/top_level_parser.dart'; | 16 import 'package:front_end/src/fasta/parser/top_level_parser.dart'; |
14 import 'package:front_end/src/fasta/scanner.dart'; | 17 import 'package:front_end/src/fasta/scanner.dart'; |
18 import 'package:front_end/src/fasta/scanner/token_constants.dart' | |
19 show STRING_TOKEN; | |
15 import 'package:front_end/src/fasta/source/directive_listener.dart'; | 20 import 'package:front_end/src/fasta/source/directive_listener.dart'; |
16 import 'package:front_end/src/fasta/translate_uri.dart'; | 21 import 'package:front_end/src/fasta/translate_uri.dart'; |
17 import 'package:kernel/target/vm.dart'; | 22 import 'package:kernel/target/vm.dart'; |
18 | 23 |
19 /// Information about a file being compiled, explicitly or implicitly. | 24 /// Information about a file being compiled, explicitly or implicitly. |
20 /// | 25 /// |
21 /// It provides a consistent view on its properties. | 26 /// It provides a consistent view on its properties. |
22 /// | 27 /// |
23 /// The properties are not guaranteed to represent the most recent state | 28 /// The properties are not guaranteed to represent the most recent state |
24 /// of the file system. To update the file to the most recent state, [refresh] | 29 /// of the file system. To update the file to the most recent state, [refresh] |
25 /// should be called. | 30 /// should be called. |
26 class FileState { | 31 class FileState { |
27 final FileSystemState _fsState; | 32 final FileSystemState _fsState; |
28 | 33 |
29 /// The absolute URI of the file. | 34 /// The absolute URI of the file. |
30 final Uri uri; | 35 final Uri uri; |
31 | 36 |
32 /// The resolved URI of the file in the file system. | 37 /// The resolved URI of the file in the file system. |
33 final Uri fileUri; | 38 final Uri fileUri; |
34 | 39 |
35 bool _exists; | 40 bool _exists; |
36 List<int> _content; | 41 List<int> _content; |
37 List<int> _contentHash; | 42 List<int> _contentHash; |
43 List<int> _apiSignature; | |
38 | 44 |
39 List<NamespaceExport> _exports; | 45 List<NamespaceExport> _exports; |
40 List<FileState> _importedLibraries; | 46 List<FileState> _importedLibraries; |
41 List<FileState> _exportedLibraries; | 47 List<FileState> _exportedLibraries; |
42 List<FileState> _partFiles; | 48 List<FileState> _partFiles; |
43 | 49 |
44 Set<FileState> _directReferencedFiles = new Set<FileState>(); | 50 Set<FileState> _directReferencedFiles = new Set<FileState>(); |
45 List<FileState> _directReferencedLibraries = <FileState>[]; | 51 List<FileState> _directReferencedLibraries = <FileState>[]; |
46 | 52 |
47 FileState._(this._fsState, this.uri, this.fileUri); | 53 FileState._(this._fsState, this.uri, this.fileUri); |
48 | 54 |
55 /// The MD5 signature of the file API. | |
56 /// It depends on all non-comment tokens outside the block bodies. | |
ahe
2017/06/08 12:33:12
How is the MD5 sum represented? As a byte array? C
scheglov
2017/06/09 18:40:59
Done.
| |
57 List<int> get apiSignature => _apiSignature; | |
58 | |
49 /// The content of the file. | 59 /// The content of the file. |
50 List<int> get content => _content; | 60 List<int> get content => _content; |
51 | 61 |
52 /// The MD5 hash of the [content]. | 62 /// The MD5 hash of the [content]. |
53 List<int> get contentHash => _contentHash; | 63 List<int> get contentHash => _contentHash; |
54 | 64 |
55 /// Libraries that this library file directly imports or exports. | 65 /// Libraries that this library file directly imports or exports. |
56 List<FileState> get directReferencedLibraries => _directReferencedLibraries; | 66 List<FileState> get directReferencedLibraries => _directReferencedLibraries; |
57 | 67 |
58 /// Whether the file exists. | 68 /// Whether the file exists. |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
110 _content = await entry.readAsBytes(); | 120 _content = await entry.readAsBytes(); |
111 _exists = true; | 121 _exists = true; |
112 } catch (_) { | 122 } catch (_) { |
113 _content = new Uint8List(0); | 123 _content = new Uint8List(0); |
114 _exists = false; | 124 _exists = false; |
115 } | 125 } |
116 | 126 |
117 // Compute the content hash. | 127 // Compute the content hash. |
118 _contentHash = md5.convert(_content).bytes; | 128 _contentHash = md5.convert(_content).bytes; |
119 | 129 |
130 // Scan the content. | |
131 ScannerResult scanResult = _scan(); | |
ahe
2017/06/08 12:33:12
Thinking out loud here, not a review comment:
Sin
scheglov
2017/06/09 18:40:59
I don't understand what this means.
| |
132 | |
133 // Compute the API signature. | |
134 _apiSignature = _computeApiSignature(scanResult.tokens); | |
135 | |
120 // Parse directives. | 136 // Parse directives. |
121 ScannerResult scannerResults = _scan(); | |
122 var listener = new _DirectiveListenerWithNative(); | 137 var listener = new _DirectiveListenerWithNative(); |
123 new TopLevelParser(listener).parseUnit(scannerResults.tokens); | 138 new TopLevelParser(listener).parseUnit(scanResult.tokens); |
124 | 139 |
125 // Build the graph. | 140 // Build the graph. |
126 _importedLibraries = <FileState>[]; | 141 _importedLibraries = <FileState>[]; |
127 _exportedLibraries = <FileState>[]; | 142 _exportedLibraries = <FileState>[]; |
128 _partFiles = <FileState>[]; | 143 _partFiles = <FileState>[]; |
129 _exports = <NamespaceExport>[]; | 144 _exports = <NamespaceExport>[]; |
130 { | 145 { |
131 FileState coreFile = await _getFileForRelativeUri('dart:core'); | 146 FileState coreFile = await _getFileForRelativeUri('dart:core'); |
132 // TODO(scheglov) add error handling | 147 // TODO(scheglov) add error handling |
133 if (coreFile != null) { | 148 if (coreFile != null) { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
180 if (uri.toString() != 'dart:core') return; | 195 if (uri.toString() != 'dart:core') return; |
181 for (String uri in new VmTarget(null).extraRequiredLibraries) { | 196 for (String uri in new VmTarget(null).extraRequiredLibraries) { |
182 FileState file = await _getFileForRelativeUri(uri); | 197 FileState file = await _getFileForRelativeUri(uri); |
183 // TODO(scheglov) add error handling | 198 // TODO(scheglov) add error handling |
184 if (file != null) { | 199 if (file != null) { |
185 _importedLibraries.add(file); | 200 _importedLibraries.add(file); |
186 } | 201 } |
187 } | 202 } |
188 } | 203 } |
189 | 204 |
205 /// Compute and return the API signature of the file. | |
206 /// | |
207 /// The signature is based on non-comment tokens of the file outside | |
208 /// of function bodies. | |
209 List<int> _computeApiSignature(Token token) { | |
210 var parser = new _BodySkippingParser(); | |
211 parser.parseUnit(token); | |
212 | |
213 ApiSignature apiSignature = new ApiSignature(); | |
214 apiSignature.addBytes(_fsState._salt); | |
215 | |
216 // Iterate over tokens and skip bodies. | |
217 Iterator<_BodyRange> bodyIterator = parser.bodyRanges.iterator; | |
218 bodyIterator.moveNext(); | |
219 for (; token.kind != EOF_TOKEN; token = token.next) { | |
220 // Move to the body range that ends after the token. | |
221 while (bodyIterator.current != null && | |
222 bodyIterator.current.last < token.charOffset) { | |
223 bodyIterator.moveNext(); | |
224 } | |
225 // If the current body range starts before or at the token, skip it. | |
226 if (bodyIterator.current != null && | |
227 bodyIterator.current.first <= token.charOffset) { | |
228 continue; | |
229 } | |
230 // The token is outside of a function body, add it. | |
231 apiSignature.addString(token.lexeme); | |
232 } | |
233 | |
234 return apiSignature.toByteList(); | |
235 } | |
236 | |
237 /// Exclude all `native 'xyz';` token sequences. | |
238 void _excludeNativeClauses(Token token) { | |
239 for (; token.kind != EOF_TOKEN; token = token.next) { | |
240 if (optional('native', token) && | |
241 token.next.kind == STRING_TOKEN && | |
242 optional(';', token.next.next)) { | |
243 token.previous.next = token.next.next; | |
244 } | |
245 } | |
246 } | |
247 | |
190 /// Return the [FileState] for the given [relativeUri] or `null` if the URI | 248 /// Return the [FileState] for the given [relativeUri] or `null` if the URI |
191 /// cannot be parsed, cannot correspond any file, etc. | 249 /// cannot be parsed, cannot correspond any file, etc. |
192 Future<FileState> _getFileForRelativeUri(String relativeUri) async { | 250 Future<FileState> _getFileForRelativeUri(String relativeUri) async { |
193 if (relativeUri.isEmpty) return null; | 251 if (relativeUri.isEmpty) return null; |
194 | 252 |
195 // Resolve the relative URI into absolute. | 253 // Resolve the relative URI into absolute. |
196 // The result is either: | 254 // The result is either: |
197 // 1) The absolute file URI. | 255 // 1) The absolute file URI. |
198 // 2) The absolute non-file URI, e.g. `package:foo/foo.dart`. | 256 // 2) The absolute non-file URI, e.g. `package:foo/foo.dart`. |
199 Uri absoluteUri; | 257 Uri absoluteUri; |
200 try { | 258 try { |
201 absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri)); | 259 absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri)); |
202 } on FormatException { | 260 } on FormatException { |
203 return null; | 261 return null; |
204 } | 262 } |
205 | 263 |
206 return await _fsState.getFile(absoluteUri); | 264 return await _fsState.getFile(absoluteUri); |
207 } | 265 } |
208 | 266 |
209 /// Scan the content of the file. | 267 /// Scan the content of the file. |
210 ScannerResult _scan() { | 268 ScannerResult _scan() { |
211 var zeroTerminatedBytes = new Uint8List(_content.length + 1); | 269 var zeroTerminatedBytes = new Uint8List(_content.length + 1); |
212 zeroTerminatedBytes.setRange(0, _content.length, _content); | 270 zeroTerminatedBytes.setRange(0, _content.length, _content); |
213 return scan(zeroTerminatedBytes); | 271 ScannerResult result = scan(zeroTerminatedBytes); |
272 _excludeNativeClauses(result.tokens); | |
273 return result; | |
214 } | 274 } |
215 } | 275 } |
216 | 276 |
217 /// Information about known file system state. | 277 /// Information about known file system state. |
218 class FileSystemState { | 278 class FileSystemState { |
219 final FileSystem fileSystem; | 279 final FileSystem fileSystem; |
220 final TranslateUri uriTranslator; | 280 final TranslateUri uriTranslator; |
281 final List<int> _salt; | |
221 | 282 |
222 _FileSystemView _fileSystemView; | 283 _FileSystemView _fileSystemView; |
223 | 284 |
224 /// Mapping from file URIs to corresponding [FileState]s. | 285 /// Mapping from file URIs to corresponding [FileState]s. |
225 final Map<Uri, FileState> _uriToFile = {}; | 286 final Map<Uri, FileState> _uriToFile = {}; |
226 | 287 |
227 /// Mapping from file URIs to corresponding [FileState]s. | 288 /// Mapping from file URIs to corresponding [FileState]s. |
228 final Map<Uri, FileState> _fileUriToFile = {}; | 289 final Map<Uri, FileState> _fileUriToFile = {}; |
229 | 290 |
230 FileSystemState(this.fileSystem, this.uriTranslator); | 291 FileSystemState(this.fileSystem, this.uriTranslator, this._salt); |
231 | 292 |
232 /// Return the [FileSystem] that is backed by this [FileSystemState]. The | 293 /// Return the [FileSystem] that is backed by this [FileSystemState]. The |
233 /// files in this [FileSystem] always have the same content as the | 294 /// files in this [FileSystem] always have the same content as the |
234 /// corresponding [FileState]s, thus avoiding race conditions when a file | 295 /// corresponding [FileState]s, thus avoiding race conditions when a file |
235 /// is updated on the actual file system. | 296 /// is updated on the actual file system. |
236 FileSystem get fileSystemView { | 297 FileSystem get fileSystemView { |
237 return _fileSystemView ??= new _FileSystemView(this); | 298 return _fileSystemView ??= new _FileSystemView(this); |
238 } | 299 } |
239 | 300 |
240 /// Return the [FileState] for the given [absoluteUri], or `null` if the | 301 /// Return the [FileState] for the given [absoluteUri], or `null` if the |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
302 } else { | 363 } else { |
303 if (combinator.names.contains(name)) { | 364 if (combinator.names.contains(name)) { |
304 return false; | 365 return false; |
305 } | 366 } |
306 } | 367 } |
307 } | 368 } |
308 return true; | 369 return true; |
309 } | 370 } |
310 } | 371 } |
311 | 372 |
373 /// The char range of a function body. | |
374 class _BodyRange { | |
375 /// The char offset of the first token in the range. | |
376 final int first; | |
377 | |
378 /// The char offset of the last token in the range. | |
379 final int last; | |
380 | |
381 _BodyRange(this.first, this.last); | |
382 | |
383 @override | |
384 String toString() => '[$first, $last]'; | |
385 } | |
386 | |
387 /// The [Parser] that skips function bodies and remembers their token ranges. | |
388 class _BodySkippingParser extends Parser { | |
389 final List<_BodyRange> bodyRanges = []; | |
390 | |
391 _BodySkippingParser() : super(new Listener()); | |
392 | |
393 @override | |
394 Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { | |
395 if (identical('{', token.lexeme)) { | |
396 Token close = skipBlock(token); | |
397 bodyRanges.add(new _BodyRange(token.charOffset, close.charOffset)); | |
398 return close; | |
399 } | |
400 return super.parseFunctionBody(token, isExpression, allowAbstract); | |
401 } | |
402 } | |
403 | |
312 /// [DirectiveListener] that skips native clauses. | 404 /// [DirectiveListener] that skips native clauses. |
313 class _DirectiveListenerWithNative extends DirectiveListener { | 405 class _DirectiveListenerWithNative extends DirectiveListener { |
314 @override | 406 @override |
315 Token handleNativeClause(Token token) => skipNativeClause(token); | 407 Token handleNativeClause(Token token) => skipNativeClause(token); |
316 } | 408 } |
317 | 409 |
318 /// [FileSystemState] based implementation of [FileSystem]. | 410 /// [FileSystemState] based implementation of [FileSystem]. |
319 /// It provides a consistent view on the known file system state. | 411 /// It provides a consistent view on the known file system state. |
320 class _FileSystemView implements FileSystem { | 412 class _FileSystemView implements FileSystem { |
321 final FileSystemState fsState; | 413 final FileSystemState fsState; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
398 node.isEvaluated = true; | 490 node.isEvaluated = true; |
399 cycle.libraries.add(node.file); | 491 cycle.libraries.add(node.file); |
400 } | 492 } |
401 topologicallySortedCycles.add(cycle); | 493 topologicallySortedCycles.add(cycle); |
402 } | 494 } |
403 | 495 |
404 _LibraryNode getNode(FileState file) { | 496 _LibraryNode getNode(FileState file) { |
405 return nodesOfFiles.putIfAbsent(file, () => new _LibraryNode(this, file)); | 497 return nodesOfFiles.putIfAbsent(file, () => new _LibraryNode(this, file)); |
406 } | 498 } |
407 } | 499 } |
OLD | NEW |