| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library code_transformer.src.resolver_impl; | 5 library code_transformer.src.resolver_impl; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'package:analyzer/analyzer.dart' show parseDirectives; | 8 import 'package:analyzer/analyzer.dart' show parseDirectives; |
| 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/constant.dart' | 10 import 'package:analyzer/src/generated/constant.dart' |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 | 24 |
| 25 // We should always be using url paths here since it's always Dart/pub code. | 25 // We should always be using url paths here since it's always Dart/pub code. |
| 26 final path = native_path.url; | 26 final path = native_path.url; |
| 27 | 27 |
| 28 /// Resolves and updates an AST based on Barback-based assets. | 28 /// Resolves and updates an AST based on Barback-based assets. |
| 29 /// | 29 /// |
| 30 /// This also provides a handful of useful APIs for traversing and working | 30 /// This also provides a handful of useful APIs for traversing and working |
| 31 /// with the resolved AST. | 31 /// with the resolved AST. |
| 32 class ResolverImpl implements Resolver { | 32 class ResolverImpl implements Resolver { |
| 33 /// Cache of all asset sources currently referenced. | 33 /// Cache of all asset sources currently referenced. |
| 34 final Map<AssetId, _AssetBasedSource> sources; | 34 final Map<AssetId, AssetBasedSource> sources; |
| 35 | 35 |
| 36 final InternalAnalysisContext _context = | 36 final InternalAnalysisContext _context = |
| 37 AnalysisEngine.instance.createAnalysisContext(); | 37 AnalysisEngine.instance.createAnalysisContext(); |
| 38 | 38 |
| 39 /// Transform for which this is currently updating, or null when not updating. | 39 /// Transform for which this is currently updating, or null when not updating. |
| 40 Transform _currentTransform; | 40 Transform _currentTransform; |
| 41 | 41 |
| 42 /// The currently resolved entry libraries, or null if nothing is resolved. | 42 /// The currently resolved entry libraries, or null if nothing is resolved. |
| 43 List<LibraryElement> _entryLibraries; | 43 List<LibraryElement> _entryLibraries; |
| 44 Set<LibraryElement> _libraries; | 44 Set<LibraryElement> _libraries; |
| 45 | 45 |
| 46 /// Future indicating when this resolver is done in the current phase. | 46 /// Future indicating when this resolver is done in the current phase. |
| 47 Future _lastPhaseComplete = new Future.value(); | 47 Future _lastPhaseComplete = new Future.value(); |
| 48 | 48 |
| 49 /// Completer for wrapping up the current phase. | 49 /// Completer for wrapping up the current phase. |
| 50 Completer _currentPhaseComplete; | 50 Completer _currentPhaseComplete; |
| 51 | 51 |
| 52 /// Creates a resolver with a given [sdk] implementation for resolving | 52 /// Creates a resolver with a given [sdk] implementation for resolving |
| 53 /// `dart:*` imports. | 53 /// `dart:*` imports. |
| 54 ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver, | 54 ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver, |
| 55 {AnalysisOptions options, Map<AssetId, _AssetBasedSource> sources}) | 55 {AnalysisOptions options, Map<AssetId, AssetBasedSource> sources}) |
| 56 : sources = sources ?? <AssetId, _AssetBasedSource>{} { | 56 : sources = sources ?? <AssetId, AssetBasedSource>{} { |
| 57 if (options == null) { | 57 if (options == null) { |
| 58 options = new AnalysisOptionsImpl() | 58 options = new AnalysisOptionsImpl() |
| 59 ..cacheSize = 256 // # of sources to cache ASTs for. | 59 ..cacheSize = 256 // # of sources to cache ASTs for. |
| 60 ..preserveComments = false | 60 ..preserveComments = false |
| 61 ..analyzeFunctionBodies = true; | 61 ..analyzeFunctionBodies = true; |
| 62 } | 62 } |
| 63 _context.analysisOptions = options; | 63 _context.analysisOptions = options; |
| 64 sdk.context.analysisOptions = options; | 64 sdk.context.analysisOptions = options; |
| 65 _context.sourceFactory = | 65 _context.sourceFactory = |
| 66 new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]); | 66 new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 var visited = new Set<AssetId>(); | 114 var visited = new Set<AssetId>(); |
| 115 var visiting = new FutureGroup(); | 115 var visiting = new FutureGroup(); |
| 116 var toUpdate = []; | 116 var toUpdate = []; |
| 117 | 117 |
| 118 void processAsset(AssetId assetId) { | 118 void processAsset(AssetId assetId) { |
| 119 visited.add(assetId); | 119 visited.add(assetId); |
| 120 | 120 |
| 121 visiting.add(transform.readInputAsString(assetId).then((contents) { | 121 visiting.add(transform.readInputAsString(assetId).then((contents) { |
| 122 var source = sources[assetId]; | 122 var source = sources[assetId]; |
| 123 if (source == null) { | 123 if (source == null) { |
| 124 source = new _AssetBasedSource(assetId, this); | 124 source = new AssetBasedSource(assetId, this); |
| 125 sources[assetId] = source; | 125 sources[assetId] = source; |
| 126 } | 126 } |
| 127 source.updateDependencies(contents); | 127 source.updateDependencies(contents); |
| 128 toUpdate.add(new _PendingUpdate(source, contents)); | 128 toUpdate.add(new _PendingUpdate(source, contents)); |
| 129 source.dependentAssets | 129 source.dependentAssets |
| 130 .where((id) => !visited.contains(id)) | 130 .where((id) => !visited.contains(id)) |
| 131 .forEach(processAsset); | 131 .forEach(processAsset); |
| 132 }, onError: (e) { | 132 }, onError: (e) { |
| 133 var source = sources[assetId]; | 133 var source = sources[assetId]; |
| 134 if (source != null && source.exists()) { | 134 if (source != null && source.exists()) { |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 250 .evaluate(expression); | 250 .evaluate(expression); |
| 251 } | 251 } |
| 252 | 252 |
| 253 Uri getImportUri(LibraryElement lib, {AssetId from}) => | 253 Uri getImportUri(LibraryElement lib, {AssetId from}) => |
| 254 _getSourceUri(lib, from: from); | 254 _getSourceUri(lib, from: from); |
| 255 | 255 |
| 256 /// Similar to getImportUri but will get the part URI for parts rather than | 256 /// Similar to getImportUri but will get the part URI for parts rather than |
| 257 /// the library URI. | 257 /// the library URI. |
| 258 Uri _getSourceUri(Element element, {AssetId from}) { | 258 Uri _getSourceUri(Element element, {AssetId from}) { |
| 259 var source = element.source; | 259 var source = element.source; |
| 260 if (source is _AssetBasedSource) { | 260 if (source is AssetBasedSource) { |
| 261 var uriString = assetIdToUri(source.assetId, from: from); | 261 var uriString = assetIdToUri(source.assetId, from: from); |
| 262 return uriString != null ? Uri.parse(uriString) : null; | 262 return uriString != null ? Uri.parse(uriString) : null; |
| 263 } else if (source is UriAnnotatedSource) { | 263 } else if (source is UriAnnotatedSource) { |
| 264 return source.uri; | 264 return source.uri; |
| 265 } | 265 } |
| 266 // Should not be able to encounter any other source types. | 266 // Should not be able to encounter any other source types. |
| 267 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); | 267 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); |
| 268 } | 268 } |
| 269 | 269 |
| 270 AssetId getSourceAssetId(Element element) { | 270 AssetId getSourceAssetId(Element element) { |
| 271 var source = element.source; | 271 var source = element.source; |
| 272 if (source is _AssetBasedSource) return source.assetId; | 272 if (source is AssetBasedSource) return source.assetId; |
| 273 return null; | 273 return null; |
| 274 } | 274 } |
| 275 | 275 |
| 276 SourceSpan getSourceSpan(Element element) { | 276 SourceSpan getSourceSpan(Element element) { |
| 277 var sourceFile = getSourceFile(element); | 277 var sourceFile = getSourceFile(element); |
| 278 if (sourceFile == null) return null; | 278 if (sourceFile == null) return null; |
| 279 return sourceFile.span( | 279 return sourceFile.span( |
| 280 element.computeNode().offset, element.computeNode().end); | 280 element.computeNode().offset, element.computeNode().end); |
| 281 } | 281 } |
| 282 | 282 |
| 283 TextEditTransaction createTextEditTransaction(Element element) { | 283 TextEditTransaction createTextEditTransaction(Element element) { |
| 284 if (element.source is! _AssetBasedSource) return null; | 284 if (element.source is! AssetBasedSource) return null; |
| 285 | 285 |
| 286 // Cannot edit unless there is an active transformer. | 286 // Cannot edit unless there is an active transformer. |
| 287 if (_currentTransform == null) return null; | 287 if (_currentTransform == null) return null; |
| 288 | 288 |
| 289 _AssetBasedSource source = element.source; | 289 AssetBasedSource source = element.source; |
| 290 // Cannot modify assets in other packages. | 290 // Cannot modify assets in other packages. |
| 291 if (source.assetId.package != _currentTransform.primaryInput.id.package) { | 291 if (source.assetId.package != _currentTransform.primaryInput.id.package) { |
| 292 return null; | 292 return null; |
| 293 } | 293 } |
| 294 | 294 |
| 295 var sourceFile = getSourceFile(element); | 295 var sourceFile = getSourceFile(element); |
| 296 if (sourceFile == null) return null; | 296 if (sourceFile == null) return null; |
| 297 | 297 |
| 298 return new TextEditTransaction(source.rawContents, sourceFile); | 298 return new TextEditTransaction(source.rawContents, sourceFile); |
| 299 } | 299 } |
| 300 | 300 |
| 301 /// Gets the SourceFile for the source of the element. | 301 /// Gets the SourceFile for the source of the element. |
| 302 SourceFile getSourceFile(Element element) { | 302 SourceFile getSourceFile(Element element) { |
| 303 var assetId = getSourceAssetId(element); | 303 var assetId = getSourceAssetId(element); |
| 304 if (assetId == null) return null; | 304 if (assetId == null) return null; |
| 305 | 305 |
| 306 var importUri = _getSourceUri(element); | 306 var importUri = _getSourceUri(element); |
| 307 var spanPath = importUri != null ? importUri.toString() : assetId.path; | 307 var spanPath = importUri != null ? importUri.toString() : assetId.path; |
| 308 return new SourceFile(sources[assetId].rawContents, url: spanPath); | 308 return new SourceFile(sources[assetId].rawContents, url: spanPath); |
| 309 } | 309 } |
| 310 } | 310 } |
| 311 | 311 |
| 312 /// Implementation of Analyzer's Source for Barback based assets. | 312 /// Implementation of Analyzer's Source for Barback based assets. |
| 313 class _AssetBasedSource extends Source { | 313 class AssetBasedSource extends Source { |
| 314 /// Asset ID where this source can be found. | 314 /// Asset ID where this source can be found. |
| 315 final AssetId assetId; | 315 final AssetId assetId; |
| 316 | 316 |
| 317 /// The resolver this is being used in. | 317 /// The resolver this is being used in. |
| 318 final ResolverImpl _resolver; | 318 final ResolverImpl _resolver; |
| 319 | 319 |
| 320 /// Cache of dependent asset IDs, to avoid re-parsing the AST. | 320 /// Cache of dependent asset IDs, to avoid re-parsing the AST. |
| 321 Iterable<AssetId> _dependentAssets; | 321 Iterable<AssetId> _dependentAssets; |
| 322 | 322 |
| 323 /// The current revision of the file, incremented only when file changes. | 323 /// The current revision of the file, incremented only when file changes. |
| 324 int _revision = 0; | 324 int _revision = 0; |
| 325 | 325 |
| 326 /// The file contents. | 326 /// The file contents. |
| 327 String _contents; | 327 String _contents; |
| 328 | 328 |
| 329 _AssetBasedSource(this.assetId, this._resolver); | 329 AssetBasedSource(this.assetId, this._resolver); |
| 330 | 330 |
| 331 /// Update the dependencies of this source. This parses [contents] but avoids | 331 /// Update the dependencies of this source. This parses [contents] but avoids |
| 332 /// any analyzer resolution. | 332 /// any analyzer resolution. |
| 333 void updateDependencies(String contents) { | 333 void updateDependencies(String contents) { |
| 334 if (contents == _contents) return; | 334 if (contents == _contents) return; |
| 335 var unit = parseDirectives(contents, suppressErrors: true); | 335 var unit = parseDirectives(contents, suppressErrors: true); |
| 336 _dependentAssets = unit.directives | 336 _dependentAssets = unit.directives |
| 337 .where((d) => (d is ImportDirective || | 337 .where((d) => d is UriBasedDirective) |
| 338 d is PartDirective || | 338 .map((d) => _resolve(assetId, (d as UriBasedDirective).uri.stringValue, |
| 339 d is ExportDirective)) | 339 _logger, _getSpan(d, contents))) |
| 340 .map((d) => _resolve( | |
| 341 assetId, d.uri.stringValue, _logger, _getSpan(d, contents))) | |
| 342 .where((id) => id != null) | 340 .where((id) => id != null) |
| 343 .toSet(); | 341 .toSet(); |
| 344 } | 342 } |
| 345 | 343 |
| 346 /// Update the contents of this file with [contents]. | 344 /// Update the contents of this file with [contents]. |
| 347 /// | 345 /// |
| 348 /// Returns true if the contents of this asset have changed. | 346 /// Returns true if the contents of this asset have changed. |
| 349 bool updateContents(String contents) { | 347 bool updateContents(String contents) { |
| 350 if (contents == _contents) return false; | 348 if (contents == _contents) return false; |
| 351 _contents = contents; | 349 _contents = contents; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 369 /// | 367 /// |
| 370 /// Only valid while the resolver is updating assets. | 368 /// Only valid while the resolver is updating assets. |
| 371 TransformLogger get _logger => _resolver._currentTransform.logger; | 369 TransformLogger get _logger => _resolver._currentTransform.logger; |
| 372 | 370 |
| 373 /// Gets all imports/parts/exports which resolve to assets (non-Dart files). | 371 /// Gets all imports/parts/exports which resolve to assets (non-Dart files). |
| 374 Iterable<AssetId> get dependentAssets => _dependentAssets; | 372 Iterable<AssetId> get dependentAssets => _dependentAssets; |
| 375 | 373 |
| 376 bool exists() => _contents != null; | 374 bool exists() => _contents != null; |
| 377 | 375 |
| 378 bool operator ==(Object other) => | 376 bool operator ==(Object other) => |
| 379 other is _AssetBasedSource && assetId == other.assetId; | 377 other is AssetBasedSource && assetId == other.assetId; |
| 380 | 378 |
| 381 int get hashCode => assetId.hashCode; | 379 int get hashCode => assetId.hashCode; |
| 382 | 380 |
| 383 void getContentsToReceiver(Source_ContentReceiver receiver) { | 381 void getContentsToReceiver(Source_ContentReceiver receiver) { |
| 384 receiver.accept(rawContents, modificationStamp); | 382 receiver.accept(rawContents, modificationStamp); |
| 385 } | 383 } |
| 386 | 384 |
| 387 String get encoding => | 385 String get encoding => |
| 388 "${uriKind.encoding}${assetId.package}/${assetId.path}"; | 386 "${uriKind.encoding}${assetId.package}/${assetId.path}"; |
| 389 | 387 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 451 assetId = _resolve(null, uri.toString(), logger, null); | 449 assetId = _resolve(null, uri.toString(), logger, null); |
| 452 if (assetId == null) { | 450 if (assetId == null) { |
| 453 logger.error('Unable to resolve asset ID for "$uri"'); | 451 logger.error('Unable to resolve asset ID for "$uri"'); |
| 454 return null; | 452 return null; |
| 455 } | 453 } |
| 456 } | 454 } |
| 457 var source = _resolver.sources[assetId]; | 455 var source = _resolver.sources[assetId]; |
| 458 // Analyzer expects that sources which are referenced but do not exist yet | 456 // Analyzer expects that sources which are referenced but do not exist yet |
| 459 // still exist, so just make an empty source. | 457 // still exist, so just make an empty source. |
| 460 if (source == null) { | 458 if (source == null) { |
| 461 source = new _AssetBasedSource(assetId, _resolver); | 459 source = new AssetBasedSource(assetId, _resolver); |
| 462 _resolver.sources[assetId] = source; | 460 _resolver.sources[assetId] = source; |
| 463 } | 461 } |
| 464 return source; | 462 return source; |
| 465 } | 463 } |
| 466 | 464 |
| 467 Source fromEncoding(UriKind kind, Uri uri) => | 465 Source fromEncoding(UriKind kind, Uri uri) => |
| 468 throw new UnsupportedError('fromEncoding is not supported'); | 466 throw new UnsupportedError('fromEncoding is not supported'); |
| 469 | 467 |
| 470 Uri restoreAbsolute(Source source) => | 468 Uri restoreAbsolute(Source source) => |
| 471 throw new UnsupportedError('restoreAbsolute is not supported'); | 469 throw new UnsupportedError('restoreAbsolute is not supported'); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 * error will be sent to the Future. | 550 * error will be sent to the Future. |
| 553 */ | 551 */ |
| 554 Future<List<E>> get future => _completer.future; | 552 Future<List<E>> get future => _completer.future; |
| 555 } | 553 } |
| 556 | 554 |
| 557 /// A pending update to notify the resolver that a [Source] has been added or | 555 /// A pending update to notify the resolver that a [Source] has been added or |
| 558 /// changed. This is used by the `_performResolve` algorithm above to apply all | 556 /// changed. This is used by the `_performResolve` algorithm above to apply all |
| 559 /// changes after it first discovers the transitive closure of files that are | 557 /// changes after it first discovers the transitive closure of files that are |
| 560 /// reachable from the sources. | 558 /// reachable from the sources. |
| 561 class _PendingUpdate { | 559 class _PendingUpdate { |
| 562 _AssetBasedSource source; | 560 AssetBasedSource source; |
| 563 String content; | 561 String content; |
| 564 | 562 |
| 565 _PendingUpdate(this.source, this.content); | 563 _PendingUpdate(this.source, this.content); |
| 566 | 564 |
| 567 void apply(ChangeSet changeSet) { | 565 void apply(ChangeSet changeSet) { |
| 568 if (!source.updateContents(content)) return; | 566 if (!source.updateContents(content)) return; |
| 569 if (source._revision == 1 && source._contents != null) { | 567 if (source._revision == 1 && source._contents != null) { |
| 570 changeSet.addedSource(source); | 568 changeSet.addedSource(source); |
| 571 } else { | 569 } else { |
| 572 changeSet.changedSource(source); | 570 changeSet.changedSource(source); |
| 573 } | 571 } |
| 574 } | 572 } |
| 575 } | 573 } |
| OLD | NEW |