| Index: code_transformers/lib/src/resolver_impl.dart
|
| diff --git a/code_transformers/lib/src/resolver_impl.dart b/code_transformers/lib/src/resolver_impl.dart
|
| deleted file mode 100644
|
| index 64e8196e6349cec87efdf8a07fd4350c8b03cdb7..0000000000000000000000000000000000000000
|
| --- a/code_transformers/lib/src/resolver_impl.dart
|
| +++ /dev/null
|
| @@ -1,553 +0,0 @@
|
| -// Copyright (c) 2014, 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.
|
| -
|
| -library code_transformer.src.resolver_impl;
|
| -
|
| -import 'dart:async';
|
| -import 'package:analyzer/analyzer.dart' show parseDirectives;
|
| -import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
|
| -import 'package:analyzer/src/generated/constant.dart'
|
| - show ConstantEvaluator, EvaluationResult;
|
| -import 'package:analyzer/src/generated/element.dart';
|
| -import 'package:analyzer/src/generated/engine.dart';
|
| -import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
|
| -import 'package:analyzer/src/generated/source.dart';
|
| -import 'package:barback/barback.dart';
|
| -import 'package:code_transformers/assets.dart';
|
| -import 'package:path/path.dart' as native_path;
|
| -import 'package:source_maps/refactor.dart';
|
| -import 'package:source_span/source_span.dart';
|
| -
|
| -import 'resolver.dart';
|
| -import 'dart_sdk.dart' show UriAnnotatedSource;
|
| -
|
| -// We should always be using url paths here since it's always Dart/pub code.
|
| -final path = native_path.url;
|
| -
|
| -/// Resolves and updates an AST based on Barback-based assets.
|
| -///
|
| -/// This also provides a handful of useful APIs for traversing and working
|
| -/// with the resolved AST.
|
| -class ResolverImpl implements Resolver {
|
| - /// Cache of all asset sources currently referenced.
|
| - final Map<AssetId, _AssetBasedSource> sources = <AssetId, _AssetBasedSource>{
|
| - };
|
| -
|
| - final InternalAnalysisContext _context =
|
| - AnalysisEngine.instance.createAnalysisContext();
|
| -
|
| - /// Transform for which this is currently updating, or null when not updating.
|
| - Transform _currentTransform;
|
| -
|
| - /// The currently resolved entry libraries, or null if nothing is resolved.
|
| - List<LibraryElement> _entryLibraries;
|
| - Set<LibraryElement> _libraries;
|
| -
|
| - /// Future indicating when this resolver is done in the current phase.
|
| - Future _lastPhaseComplete = new Future.value();
|
| -
|
| - /// Completer for wrapping up the current phase.
|
| - Completer _currentPhaseComplete;
|
| -
|
| - /// Creates a resolver with a given [sdk] implementation for resolving
|
| - /// `dart:*` imports.
|
| - ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver,
|
| - {AnalysisOptions options}) {
|
| - if (options == null) {
|
| - options = new AnalysisOptionsImpl()
|
| - ..cacheSize = 256 // # of sources to cache ASTs for.
|
| - ..preserveComments = false
|
| - ..analyzeFunctionBodies = true;
|
| - }
|
| - _context.analysisOptions = options;
|
| - sdk.context.analysisOptions = options;
|
| - _context.sourceFactory =
|
| - new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]);
|
| - }
|
| -
|
| - LibraryElement getLibrary(AssetId assetId) {
|
| - var source = sources[assetId];
|
| - return source == null ? null : _context.computeLibraryElement(source);
|
| - }
|
| -
|
| - Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) {
|
| - // Can only have one resolve in progress at a time, so chain the current
|
| - // resolution to be after the last one.
|
| - var phaseComplete = new Completer();
|
| - var future = _lastPhaseComplete.whenComplete(() {
|
| - _currentPhaseComplete = phaseComplete;
|
| - return _performResolve(transform,
|
| - entryPoints == null ? [transform.primaryInput.id] : entryPoints);
|
| - }).then((_) => this);
|
| - // Advance the lastPhaseComplete to be done when this phase is all done.
|
| - _lastPhaseComplete = phaseComplete.future;
|
| - return future;
|
| - }
|
| -
|
| - void release() {
|
| - if (_currentPhaseComplete == null) {
|
| - throw new StateError('Releasing without current lock.');
|
| - }
|
| - _currentPhaseComplete.complete(null);
|
| - _currentPhaseComplete = null;
|
| -
|
| - // Clear out libraries since they should not be referenced after release.
|
| - _entryLibraries = null;
|
| - _libraries = null;
|
| - _currentTransform = null;
|
| - }
|
| -
|
| - Future _performResolve(Transform transform, List<AssetId> entryPoints) {
|
| - if (_currentTransform != null) {
|
| - throw new StateError('Cannot be accessed by concurrent transforms');
|
| - }
|
| - _currentTransform = transform;
|
| -
|
| - // Basic approach is to start at the first file, update it's contents
|
| - // and see if it changed, then walk all files accessed by it.
|
| - var visited = new Set<AssetId>();
|
| - var visiting = new FutureGroup();
|
| - var toUpdate = [];
|
| -
|
| - void processAsset(AssetId assetId) {
|
| - visited.add(assetId);
|
| -
|
| - visiting.add(transform.readInputAsString(assetId).then((contents) {
|
| - var source = sources[assetId];
|
| - if (source == null) {
|
| - source = new _AssetBasedSource(assetId, this);
|
| - sources[assetId] = source;
|
| - }
|
| - source.updateDependencies(contents);
|
| - toUpdate.add(new _PendingUpdate(source, contents));
|
| - source.dependentAssets
|
| - .where((id) => !visited.contains(id))
|
| - .forEach(processAsset);
|
| - }, onError: (e) {
|
| - var source = sources[assetId];
|
| - if (source != null && source.exists()) {
|
| - _context.applyChanges(new ChangeSet()..removedSource(source));
|
| - sources[assetId].updateContents(null);
|
| - }
|
| - }));
|
| - }
|
| - entryPoints.forEach(processAsset);
|
| -
|
| - // Once we have all asset sources updated with the new contents then
|
| - // resolve everything.
|
| - return visiting.future.then((_) {
|
| - var changeSet = new ChangeSet();
|
| - toUpdate.forEach((pending) => pending.apply(changeSet));
|
| - var unreachableAssets =
|
| - sources.keys.toSet().difference(visited).map((id) => sources[id]);
|
| - for (var unreachable in unreachableAssets) {
|
| - changeSet.removedSource(unreachable);
|
| - unreachable.updateContents(null);
|
| - sources.remove(unreachable.assetId);
|
| - }
|
| -
|
| - // Update the analyzer context with the latest sources
|
| - _context.applyChanges(changeSet);
|
| - // Force resolve each entry point (the getter will ensure the library is
|
| - // computed first).
|
| - _entryLibraries = entryPoints.map((id) {
|
| - var source = sources[id];
|
| - if (source == null) return null;
|
| - return _context.computeLibraryElement(source);
|
| - }).toList();
|
| - });
|
| - }
|
| -
|
| - Iterable<LibraryElement> get libraries {
|
| - if (_libraries == null) {
|
| - // Note: we don't use `lib.visibleLibraries` because that excludes the
|
| - // exports seen in the entry libraries.
|
| - _libraries = new Set<LibraryElement>();
|
| - _entryLibraries.forEach(_collectLibraries);
|
| - }
|
| - return _libraries;
|
| - }
|
| -
|
| - void _collectLibraries(LibraryElement lib) {
|
| - if (lib == null || _libraries.contains(lib)) return;
|
| - _libraries.add(lib);
|
| - lib.importedLibraries.forEach(_collectLibraries);
|
| - lib.exportedLibraries.forEach(_collectLibraries);
|
| - }
|
| -
|
| - LibraryElement getLibraryByName(String libraryName) =>
|
| - libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
|
| -
|
| - LibraryElement getLibraryByUri(Uri uri) =>
|
| - libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
|
| -
|
| - ClassElement getType(String typeName) {
|
| - var dotIndex = typeName.lastIndexOf('.');
|
| - var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
|
| -
|
| - var className =
|
| - dotIndex == -1 ? typeName : typeName.substring(dotIndex + 1);
|
| -
|
| - for (var lib in libraries.where((l) => l.name == libraryName)) {
|
| - var type = lib.getType(className);
|
| - if (type != null) return type;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - Element getLibraryVariable(String variableName) {
|
| - var dotIndex = variableName.lastIndexOf('.');
|
| - var libraryName = dotIndex == -1 ? '' : variableName.substring(0, dotIndex);
|
| -
|
| - var name =
|
| - dotIndex == -1 ? variableName : variableName.substring(dotIndex + 1);
|
| -
|
| - return libraries
|
| - .where((lib) => lib.name == libraryName)
|
| - .expand((lib) => lib.units)
|
| - .expand((unit) => unit.topLevelVariables)
|
| - .firstWhere((variable) => variable.name == name, orElse: () => null);
|
| - }
|
| -
|
| - Element getLibraryFunction(String fnName) {
|
| - var dotIndex = fnName.lastIndexOf('.');
|
| - var libraryName = dotIndex == -1 ? '' : fnName.substring(0, dotIndex);
|
| -
|
| - var name = dotIndex == -1 ? fnName : fnName.substring(dotIndex + 1);
|
| -
|
| - return libraries
|
| - .where((lib) => lib.name == libraryName)
|
| - .expand((lib) => lib.units)
|
| - .expand((unit) => unit.functions)
|
| - .firstWhere((fn) => fn.name == name, orElse: () => null);
|
| - }
|
| -
|
| - EvaluationResult evaluateConstant(
|
| - LibraryElement library, Expression expression) {
|
| - return new ConstantEvaluator(library.source, _context.typeProvider)
|
| - .evaluate(expression);
|
| - }
|
| -
|
| - Uri getImportUri(LibraryElement lib, {AssetId from}) =>
|
| - _getSourceUri(lib, from: from);
|
| -
|
| - /// Similar to getImportUri but will get the part URI for parts rather than
|
| - /// the library URI.
|
| - Uri _getSourceUri(Element element, {AssetId from}) {
|
| - var source = element.source;
|
| - if (source is _AssetBasedSource) {
|
| - var uriString = assetIdToUri(source.assetId, from: from);
|
| - return uriString != null ? Uri.parse(uriString) : null;
|
| - } else if (source is UriAnnotatedSource) {
|
| - return source.uri;
|
| - }
|
| - // Should not be able to encounter any other source types.
|
| - throw new StateError('Unable to resolve URI for ${source.runtimeType}');
|
| - }
|
| -
|
| - AssetId getSourceAssetId(Element element) {
|
| - var source = element.source;
|
| - if (source is _AssetBasedSource) return source.assetId;
|
| - return null;
|
| - }
|
| -
|
| - SourceSpan getSourceSpan(Element element) {
|
| - var sourceFile = getSourceFile(element);
|
| - if (sourceFile == null) return null;
|
| - return sourceFile.span(element.node.offset, element.node.end);
|
| - }
|
| -
|
| - TextEditTransaction createTextEditTransaction(Element element) {
|
| - if (element.source is! _AssetBasedSource) return null;
|
| -
|
| - // Cannot edit unless there is an active transformer.
|
| - if (_currentTransform == null) return null;
|
| -
|
| - _AssetBasedSource source = element.source;
|
| - // Cannot modify assets in other packages.
|
| - if (source.assetId.package != _currentTransform.primaryInput.id.package) {
|
| - return null;
|
| - }
|
| -
|
| - var sourceFile = getSourceFile(element);
|
| - if (sourceFile == null) return null;
|
| -
|
| - return new TextEditTransaction(source.rawContents, sourceFile);
|
| - }
|
| -
|
| - /// Gets the SourceFile for the source of the element.
|
| - SourceFile getSourceFile(Element element) {
|
| - var assetId = getSourceAssetId(element);
|
| - if (assetId == null) return null;
|
| -
|
| - var importUri = _getSourceUri(element);
|
| - var spanPath = importUri != null ? importUri.toString() : assetId.path;
|
| - return new SourceFile(sources[assetId].rawContents, url: spanPath);
|
| - }
|
| -}
|
| -
|
| -/// Implementation of Analyzer's Source for Barback based assets.
|
| -class _AssetBasedSource extends Source {
|
| -
|
| - /// Asset ID where this source can be found.
|
| - final AssetId assetId;
|
| -
|
| - /// The resolver this is being used in.
|
| - final ResolverImpl _resolver;
|
| -
|
| - /// Cache of dependent asset IDs, to avoid re-parsing the AST.
|
| - Iterable<AssetId> _dependentAssets;
|
| -
|
| - /// The current revision of the file, incremented only when file changes.
|
| - int _revision = 0;
|
| -
|
| - /// The file contents.
|
| - String _contents;
|
| -
|
| - _AssetBasedSource(this.assetId, this._resolver);
|
| -
|
| - /// Update the dependencies of this source. This parses [contents] but avoids
|
| - /// any analyzer resolution.
|
| - void updateDependencies(String contents) {
|
| - if (contents == _contents) return;
|
| - var unit = parseDirectives(contents, suppressErrors: true);
|
| - _dependentAssets = unit.directives
|
| - .where((d) => (d is ImportDirective ||
|
| - d is PartDirective ||
|
| - d is ExportDirective))
|
| - .map((d) => _resolve(
|
| - assetId, d.uri.stringValue, _logger, _getSpan(d, contents)))
|
| - .where((id) => id != null)
|
| - .toSet();
|
| - }
|
| -
|
| - /// Update the contents of this file with [contents].
|
| - ///
|
| - /// Returns true if the contents of this asset have changed.
|
| - bool updateContents(String contents) {
|
| - if (contents == _contents) return false;
|
| - _contents = contents;
|
| - ++_revision;
|
| - return true;
|
| - }
|
| -
|
| - /// Contents of the file.
|
| - TimestampedData<String> get contents {
|
| - if (!exists()) throw new StateError('$assetId does not exist');
|
| -
|
| - return new TimestampedData<String>(modificationStamp, _contents);
|
| - }
|
| -
|
| - /// Contents of the file.
|
| - String get rawContents => _contents;
|
| -
|
| - Uri get uri => Uri.parse('asset:${assetId.package}/${assetId.path}');
|
| -
|
| - /// Logger for the current transform.
|
| - ///
|
| - /// Only valid while the resolver is updating assets.
|
| - TransformLogger get _logger => _resolver._currentTransform.logger;
|
| -
|
| - /// Gets all imports/parts/exports which resolve to assets (non-Dart files).
|
| - Iterable<AssetId> get dependentAssets => _dependentAssets;
|
| -
|
| - bool exists() => _contents != null;
|
| -
|
| - bool operator ==(Object other) =>
|
| - other is _AssetBasedSource && assetId == other.assetId;
|
| -
|
| - int get hashCode => assetId.hashCode;
|
| -
|
| - void getContentsToReceiver(Source_ContentReceiver receiver) {
|
| - receiver.accept(rawContents, modificationStamp);
|
| - }
|
| -
|
| - String get encoding =>
|
| - "${uriKind.encoding}${assetId.package}/${assetId.path}";
|
| -
|
| - String get fullName => assetId.toString();
|
| -
|
| - int get modificationStamp => _revision;
|
| -
|
| - String get shortName => path.basename(assetId.path);
|
| -
|
| - UriKind get uriKind {
|
| - if (assetId.path.startsWith('lib/')) return UriKind.PACKAGE_URI;
|
| - return UriKind.FILE_URI;
|
| - }
|
| -
|
| - bool get isInSystemLibrary => false;
|
| -
|
| - Source resolveRelative(Uri relativeUri) {
|
| - var id = _resolve(assetId, relativeUri.toString(), _logger, null);
|
| - if (id == null) return null;
|
| -
|
| - // The entire AST should have been parsed and loaded at this point.
|
| - var source = _resolver.sources[id];
|
| - if (source == null) {
|
| - _logger.error('Could not load asset $id');
|
| - }
|
| - return source;
|
| - }
|
| -
|
| - Uri resolveRelativeUri(Uri relativeUri) {
|
| - var id = _resolve(assetId, relativeUri.toString(), _logger, null);
|
| - if (id == null) return uri.resolveUri(relativeUri);
|
| -
|
| - // The entire AST should have been parsed and loaded at this point.
|
| - var source = _resolver.sources[id];
|
| - if (source == null) {
|
| - _logger.error('Could not load asset $id');
|
| - }
|
| - return source.uri;
|
| - }
|
| -
|
| - /// For logging errors.
|
| - SourceSpan _getSpan(AstNode node, [String contents]) =>
|
| - _getSourceFile(contents).span(node.offset, node.end);
|
| - /// For logging errors.
|
| - SourceFile _getSourceFile([String contents]) {
|
| - var uri = assetIdToUri(assetId);
|
| - var path = uri != null ? uri : assetId.path;
|
| - return new SourceFile(contents != null ? contents : rawContents, url: path);
|
| - }
|
| -}
|
| -
|
| -/// Implementation of Analyzer's UriResolver for Barback based assets.
|
| -class _AssetUriResolver implements UriResolver {
|
| - final ResolverImpl _resolver;
|
| - _AssetUriResolver(this._resolver);
|
| -
|
| - Source resolveAbsolute(Uri uri) {
|
| - assert(uri.scheme != 'dart');
|
| - var assetId;
|
| - if (uri.scheme == 'asset') {
|
| - var parts = path.split(uri.path);
|
| - assetId = new AssetId(parts[0], path.joinAll(parts.skip(1)));
|
| - } else {
|
| - assetId = _resolve(null, uri.toString(), logger, null);
|
| - if (assetId == null) {
|
| - logger.error('Unable to resolve asset ID for "$uri"');
|
| - return null;
|
| - }
|
| - }
|
| - var source = _resolver.sources[assetId];
|
| - // Analyzer expects that sources which are referenced but do not exist yet
|
| - // still exist, so just make an empty source.
|
| - if (source == null) {
|
| - source = new _AssetBasedSource(assetId, _resolver);
|
| - _resolver.sources[assetId] = source;
|
| - }
|
| - return source;
|
| - }
|
| -
|
| - Source fromEncoding(UriKind kind, Uri uri) =>
|
| - throw new UnsupportedError('fromEncoding is not supported');
|
| -
|
| - Uri restoreAbsolute(Source source) =>
|
| - throw new UnsupportedError('restoreAbsolute is not supported');
|
| -
|
| - TransformLogger get logger => _resolver._currentTransform.logger;
|
| -}
|
| -
|
| -/// Get an asset ID for a URL relative to another source asset.
|
| -AssetId _resolve(
|
| - AssetId source, String url, TransformLogger logger, SourceSpan span) {
|
| - if (url == null || url == '') return null;
|
| - var uri = Uri.parse(url);
|
| -
|
| - // Workaround for dartbug.com/17156- pub transforms package: imports from
|
| - // files of the transformers package to have absolute /packages/ URIs.
|
| - if (uri.scheme == '' &&
|
| - path.isAbsolute(url) &&
|
| - uri.pathSegments[0] == 'packages') {
|
| - uri = Uri.parse('package:${uri.pathSegments.skip(1).join(path.separator)}');
|
| - }
|
| -
|
| - if (uri.scheme == 'package') {
|
| - var segments = new List.from(uri.pathSegments);
|
| - var package = segments[0];
|
| - segments[0] = 'lib';
|
| - return new AssetId(package, segments.join(path.separator));
|
| - }
|
| - // Dart SDK libraries do not have assets.
|
| - if (uri.scheme == 'dart') return null;
|
| -
|
| - return uriToAssetId(source, url, logger, span);
|
| -}
|
| -
|
| -/// A completer that waits until all added [Future]s complete.
|
| -// TODO(blois): Copied from quiver. Remove from here when it gets
|
| -// added to dart:core. (See #6626.)
|
| -class FutureGroup<E> {
|
| - static const _FINISHED = -1;
|
| -
|
| - int _pending = 0;
|
| - Future _failedTask;
|
| - final Completer<List> _completer = new Completer<List>();
|
| - final List results = [];
|
| -
|
| - /** Gets the task that failed, if any. */
|
| - Future get failedTask => _failedTask;
|
| -
|
| - /**
|
| - * Wait for [task] to complete.
|
| - *
|
| - * If this group has already been marked as completed, a [StateError] will be
|
| - * thrown.
|
| - *
|
| - * If this group has a [failedTask], new tasks will be ignored, because the
|
| - * error has already been signaled.
|
| - */
|
| - void add(Future task) {
|
| - if (_failedTask != null) return;
|
| - if (_pending == _FINISHED) throw new StateError("Future already completed");
|
| -
|
| - _pending++;
|
| - var i = results.length;
|
| - results.add(null);
|
| - task.then((res) {
|
| - results[i] = res;
|
| - if (_failedTask != null) return;
|
| - _pending--;
|
| - if (_pending == 0) {
|
| - _pending = _FINISHED;
|
| - _completer.complete(results);
|
| - }
|
| - }, onError: (e, s) {
|
| - if (_failedTask != null) return;
|
| - _failedTask = task;
|
| - _completer.completeError(e, s);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * A Future that completes with a List of the values from all the added
|
| - * tasks, when they have all completed.
|
| - *
|
| - * If any task fails, this Future will receive the error. Only the first
|
| - * error will be sent to the Future.
|
| - */
|
| - Future<List<E>> get future => _completer.future;
|
| -}
|
| -
|
| -/// A pending update to notify the resolver that a [Source] has been added or
|
| -/// changed. This is used by the `_performResolve` algorithm above to apply all
|
| -/// changes after it first discovers the transitive closure of files that are
|
| -/// reachable from the sources.
|
| -class _PendingUpdate {
|
| - _AssetBasedSource source;
|
| - String content;
|
| -
|
| - _PendingUpdate(this.source, this.content);
|
| -
|
| - void apply(ChangeSet changeSet) {
|
| - if (!source.updateContents(content)) return;
|
| - if (source._revision == 1 && source._contents != null) {
|
| - changeSet.addedSource(source);
|
| - } else {
|
| - changeSet.changedSource(source);
|
| - }
|
| - }
|
| -}
|
|
|