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

Side by Side Diff: pkg/code_transformers/lib/src/resolver_impl.dart

Issue 174053004: Revert "Adding package:code_transformers for unifying common transformers code" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library code_transformer.src.resolver_impl;
6
7 import 'dart:async';
8 import 'package:analyzer/src/generated/ast.dart';
9 import 'package:analyzer/src/generated/element.dart';
10 import 'package:analyzer/src/generated/engine.dart';
11 import 'package:analyzer/src/generated/error.dart';
12 import 'package:analyzer/src/generated/java_io.dart';
13 import 'package:analyzer/src/generated/parser.dart' show Parser;
14 import 'package:analyzer/src/generated/scanner.dart';
15 import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
16 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
17 import 'package:analyzer/src/generated/source.dart';
18 import 'package:barback/barback.dart';
19 import 'package:path/path.dart' as path;
20 import 'package:source_maps/refactor.dart';
21 import 'package:source_maps/span.dart' show SourceFile, Span;
22
23 import 'resolver.dart';
24
25 /// Resolves and updates an AST based on Barback-based assets.
26 ///
27 /// This also provides a handful of useful APIs for traversing and working
28 /// with the resolved AST.
29 class ResolverImpl implements Resolver {
30 /// Cache of all asset sources currently referenced.
31 final Map<AssetId, _AssetBasedSource> sources =
32 <AssetId, _AssetBasedSource>{};
33
34 /// The Dart entry point file where parsing begins.
35 final AssetId entryPoint;
36
37 final AnalysisContext _context =
38 AnalysisEngine.instance.createAnalysisContext();
39
40 /// Transform for which this is currently updating, or null when not updating.
41 Transform _currentTransform;
42
43 /// The currently resolved library, or null if unresolved.
44 LibraryElement _entryLibrary;
45
46 /// Handler for all Dart SDK (dart:) sources.
47 DirectoryBasedDartSdk _dartSdk;
48
49 /// Creates a resolver that will resolve the Dart code starting at
50 /// [entryPoint].
51 ///
52 /// [sdkDir] is the root directory of the Dart SDK, for resolving dart:
53 /// imports.
54 ResolverImpl(this.entryPoint, String sdkDir, {AnalysisOptions options}) {
55 if (options == null) {
56 options = new AnalysisOptionsImpl()
57 ..cacheSize = 256 // # of sources to cache ASTs for.
58 ..preserveComments = false
59 ..analyzeFunctionBodies = true;
60 }
61 _context.analysisOptions = options;
62
63 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir));
64 _dartSdk.context.analysisOptions = options;
65
66 _context.sourceFactory = new SourceFactory.con2([
67 new DartUriResolverProxy(_dartSdk),
68 new _AssetUriResolver(this)]);
69 }
70
71 LibraryElement get entryLibrary => _entryLibrary;
72
73
74 /// Update the status of all the sources referenced by the entryPoint and
75 /// update the resolved library.
76 ///
77 /// This will be invoked automatically by [ResolverTransformer]. Only one
78 /// transformer may update this at a time.
79 Future updateSources(Transform transform) {
80 if (_currentTransform != null) {
81 throw new StateError('Cannot be accessed by concurrent transforms');
82 }
83 _currentTransform = transform;
84 // Clear this out and update once all asset changes have been processed.
85 _entryLibrary = null;
86
87 // Basic approach is to start at the first file, update it's contents
88 // and see if it changed, then walk all files accessed by it.
89 var visited = new Set<AssetId>();
90 var visiting = new FutureGroup();
91
92 void processAsset(AssetId assetId) {
93 visited.add(assetId);
94
95 visiting.add(transform.readInputAsString(assetId).then((contents) {
96 var source = sources[assetId];
97 if (source == null) {
98 source = new _AssetBasedSource(assetId, this);
99 sources[assetId] = source;
100 }
101 source.updateContents(contents);
102
103 source.dependentAssets
104 .where((id) => !visited.contains(id))
105 .forEach(processAsset);
106
107 }, onError: (e) {
108 _context.applyChanges(new ChangeSet()..removed(sources[assetId]));
109 sources.remove(assetId);
110 }));
111 }
112 processAsset(entryPoint);
113
114 // Once we have all asset sources updated with the new contents then
115 // resolve everything.
116 return visiting.future.then((_) {
117 var changeSet = new ChangeSet();
118 var unreachableAssets = new Set.from(sources.keys).difference(visited);
119 for (var unreachable in unreachableAssets) {
120 changeSet.removed(sources[unreachable]);
121 sources.remove(unreachable);
122 }
123
124 // Update the analyzer context with the latest sources
125 _context.applyChanges(changeSet);
126 // Resolve the AST
127 _entryLibrary = _context.computeLibraryElement(sources[entryPoint]);
128 _currentTransform = null;
129 });
130 }
131
132 Iterable<LibraryElement> get libraries => entryLibrary.visibleLibraries;
133
134 LibraryElement getLibraryByName(String libraryName) =>
135 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
136
137 LibraryElement getLibraryByUri(Uri uri) =>
138 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
139
140 ClassElement getType(String typeName) {
141 var dotIndex = typeName.lastIndexOf('.');
142 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
143
144 var className = dotIndex == -1 ?
145 typeName : typeName.substring(dotIndex + 1);
146
147 for (var lib in libraries.where((l) => l.name == libraryName)) {
148 var type = lib.getType(className);
149 if (type != null) return type;
150 }
151 return null;
152 }
153
154 Element getLibraryVariable(String variableName) {
155 var dotIndex = variableName.lastIndexOf('.');
156 var libraryName = dotIndex == -1 ? '' : variableName.substring(0, dotIndex);
157
158 var name = dotIndex == -1 ?
159 variableName : variableName.substring(dotIndex + 1);
160
161 return libraries.where((lib) => lib.name == libraryName)
162 .expand((lib) => lib.units)
163 .expand((unit) => unit.topLevelVariables)
164 .firstWhere((variable) => variable.name == name,
165 orElse: () => null);
166 }
167
168 Element getLibraryFunction(String fnName) {
169 var dotIndex = fnName.lastIndexOf('.');
170 var libraryName = dotIndex == -1 ? '' : fnName.substring(0, dotIndex);
171
172 var name = dotIndex == -1 ?
173 fnName : fnName.substring(dotIndex + 1);
174
175 return libraries.where((lib) => lib.name == libraryName)
176 .expand((lib) => lib.units)
177 .expand((unit) => unit.functions)
178 .firstWhere((fn) => fn.name == name,
179 orElse: () => null);
180 }
181
182 Uri getImportUri(LibraryElement lib, {AssetId from}) =>
183 _getSourceUri(lib, from: from);
184
185
186 /// Similar to getImportUri but will get the part URI for parts rather than
187 /// the library URI.
188 Uri _getSourceUri(Element element, {AssetId from}) {
189 var source = element.source;
190 if (source is _AssetBasedSource) {
191 return source.getSourceUri(from);
192 } else if (source is _DartSourceProxy) {
193 return source.uri;
194 }
195 // Should not be able to encounter any other source types.
196 throw new StateError('Unable to resolve URI for ${source.runtimeType}');
197 }
198
199 AssetId getSourceAssetId(Element element) {
200 var source = element.source;
201 if (source is _AssetBasedSource) return source.assetId;
202 return null;
203 }
204
205 Span getSourceSpan(Element element) {
206 var sourceFile = _getSourceFile(element);
207 if (sourceFile == null) return null;
208 return sourceFile.span(element.node.offset, element.node.end);
209 }
210
211 TextEditTransaction createTextEditTransaction(Element element) {
212 if (element.source is! _AssetBasedSource) return null;
213
214 _AssetBasedSource source = element.source;
215 // Cannot modify assets in other packages.
216 if (source.assetId.package != entryPoint.package) return null;
217
218 var sourceFile = _getSourceFile(element);
219 if (sourceFile == null) return null;
220
221 return new TextEditTransaction(source.contents, sourceFile);
222 }
223
224 /// Gets the SourceFile for the source of the element.
225 SourceFile _getSourceFile(Element element) {
226 var assetId = getSourceAssetId(element);
227 if (assetId == null) return null;
228
229 var importUri = _getSourceUri(element, from: entryPoint);
230 var spanPath = importUri != null ? importUri.toString() : assetId.path;
231 return new SourceFile.text(spanPath, sources[assetId].contents);
232 }
233 }
234
235 /// Implementation of Analyzer's Source for Barback based assets.
236 class _AssetBasedSource extends Source {
237
238 /// Asset ID where this source can be found.
239 final AssetId assetId;
240
241 /// The resolver this is being used in.
242 final ResolverImpl _resolver;
243
244 /// Cache of dependent asset IDs, to avoid re-parsing the AST.
245 Iterable<AssetId> _dependentAssets;
246
247 /// The current revision of the file, incremented only when file changes.
248 int _revision = 0;
249
250 /// The file contents.
251 String _contents;
252
253 _AssetBasedSource(this.assetId, this._resolver);
254
255 /// Update the contents of this file with [contents].
256 ///
257 /// Returns true if the contents of this asset have changed.
258 bool updateContents(String contents) {
259 if (contents == _contents) return false;
260 var added = _contents == null;
261 _contents = contents;
262 ++_revision;
263 // Invalidate the imports so we only parse the AST when needed.
264 _dependentAssets = null;
265
266 if (added) {
267 _resolver._context.applyChanges(new ChangeSet()..added(this));
268 } else {
269 _resolver._context.applyChanges(new ChangeSet()..changed(this));
270 }
271
272 var compilationUnit = _resolver._context.parseCompilationUnit(this);
273 _dependentAssets = compilationUnit.directives
274 .where((d) => (d is ImportDirective || d is PartDirective ||
275 d is ExportDirective))
276 .map((d) => _resolve(assetId, d.uri.stringValue,
277 _logger, _getSpan(d)))
278 .where((id) => id != null).toSet();
279 return true;
280 }
281
282 /// Contents of the file.
283 String get contents => _contents;
284
285 /// Logger for the current transform.
286 ///
287 /// Only valid while the resolver is updating assets.
288 TransformLogger get _logger => _resolver._currentTransform.logger;
289
290 /// Gets all imports/parts/exports which resolve to assets (non-Dart files).
291 Iterable<AssetId> get dependentAssets => _dependentAssets;
292
293 bool exists() => true;
294
295 bool operator ==(Object other) =>
296 other is _AssetBasedSource && assetId == other.assetId;
297
298 int get hashCode => assetId.hashCode;
299
300 void getContents(Source_ContentReceiver receiver) {
301 receiver.accept(contents, modificationStamp);
302 }
303
304 String get encoding =>
305 "${uriKind.encoding}${assetId.package}/${assetId.path}";
306
307 String get fullName => assetId.toString();
308
309 int get modificationStamp => _revision;
310
311 String get shortName => path.basename(assetId.path);
312
313 UriKind get uriKind {
314 if (assetId.path.startsWith('lib/')) return UriKind.PACKAGE_URI;
315 return UriKind.FILE_URI;
316 }
317
318 bool get isInSystemLibrary => false;
319
320 Source resolveRelative(Uri relativeUri) {
321 var id = _resolve(assetId, relativeUri.toString(), _logger, null);
322 if (id == null) return null;
323
324 // The entire AST should have been parsed and loaded at this point.
325 var source = _resolver.sources[id];
326 if (source == null) {
327 _logger.error('Could not load asset $id');
328 }
329 return source;
330 }
331
332 /// For logging errors.
333 Span _getSpan(ASTNode node) => _sourceFile.span(node.offset, node.end);
334 /// For logging errors.
335 SourceFile get _sourceFile {
336 var uri = getSourceUri(_resolver.entryPoint);
337 var path = uri != null ? uri.toString() : assetId.path;
338
339 return new SourceFile.text(path, contents);
340 }
341
342 /// Gets a URI which would be appropriate for importing this file.
343 ///
344 /// Note that this file may represent a non-importable file such as a part.
345 Uri getSourceUri([AssetId from]) {
346 if (!assetId.path.startsWith('lib/')) {
347 // Cannot do absolute imports of non lib-based assets.
348 if (from == null) return null;
349
350 if (assetId.package != from.package) return null;
351 return new Uri(
352 path: path.relative(assetId.path, from: path.dirname(from.path)));
353 }
354
355 return Uri.parse('package:${assetId.package}/${assetId.path.substring(4)}');
356 }
357 }
358
359 /// Implementation of Analyzer's UriResolver for Barback based assets.
360 class _AssetUriResolver implements UriResolver {
361 final ResolverImpl _resolver;
362 _AssetUriResolver(this._resolver);
363
364 Source resolveAbsolute(ContentCache contentCache, Uri uri) {
365 var assetId = _resolve(null, uri.toString(), logger, null);
366 var source = _resolver.sources[assetId];
367 /// All resolved assets should be available by this point.
368 if (source == null) {
369 logger.error('Unable to find asset for "$uri"');
370 }
371 return source;
372 }
373
374 Source fromEncoding(ContentCache contentCache, UriKind kind, Uri uri) =>
375 throw new UnsupportedError('fromEncoding is not supported');
376
377 Uri restoreAbsolute(Source source) =>
378 throw new UnsupportedError('restoreAbsolute is not supported');
379
380 TransformLogger get logger => _resolver._currentTransform.logger;
381 }
382
383
384 /// Dart SDK which wraps all Dart sources to ensure they are tracked with Uris.
385 ///
386 /// Just a simple wrapper to make it easy to make sure that all sources we
387 /// encounter are either _AssetBasedSource or _DartSourceProxy.
388 class _DirectoryBasedDartSdkProxy extends DirectoryBasedDartSdk {
389 _DirectoryBasedDartSdkProxy(JavaFile sdkDirectory) : super(sdkDirectory);
390
391 Source mapDartUri(String dartUri) =>
392 _DartSourceProxy.wrap(super.mapDartUri(dartUri), Uri.parse(dartUri));
393 }
394
395
396 /// Dart SDK resolver which wraps all Dart sources to ensure they are tracked
397 /// with URIs.
398 class DartUriResolverProxy implements DartUriResolver {
399 final DartUriResolver _proxy;
400 DartUriResolverProxy(DirectoryBasedDartSdk sdk) :
401 _proxy = new DartUriResolver(sdk);
402
403 Source resolveAbsolute(ContentCache contentCache, Uri uri) =>
404 _DartSourceProxy.wrap(_proxy.resolveAbsolute(contentCache, uri), uri);
405
406 DartSdk get dartSdk => _proxy.dartSdk;
407
408 Source fromEncoding(ContentCache contentCache, UriKind kind, Uri uri) =>
409 throw new UnsupportedError('fromEncoding is not supported');
410
411 Uri restoreAbsolute(Source source) =>
412 throw new UnsupportedError('restoreAbsolute is not supported');
413 }
414
415 /// Source file for dart: sources which track the sources with dart: URIs.
416 ///
417 /// This is primarily to support [Resolver.getImportUri] for Dart SDK (dart:)
418 /// based libraries.
419 class _DartSourceProxy implements Source {
420
421 /// Absolute URI which this source can be imported from
422 final Uri uri;
423
424 /// Underlying source object.
425 final Source _proxy;
426
427 _DartSourceProxy(this._proxy, this.uri);
428
429 /// Ensures that [source] is a _DartSourceProxy.
430 static _DartSourceProxy wrap(Source source, Uri uri) {
431 if (source == null || source is _DartSourceProxy) return source;
432 return new _DartSourceProxy(source, uri);
433 }
434
435 Source resolveRelative(Uri relativeUri) {
436 // Assume that the type can be accessed via this URI, since these
437 // should only be parts for dart core files.
438 return wrap(_proxy.resolveRelative(relativeUri), uri);
439 }
440
441 bool exists() => _proxy.exists();
442
443 bool operator ==(Object other) =>
444 (other is _DartSourceProxy && _proxy == other._proxy);
445
446 int get hashCode => _proxy.hashCode;
447
448 void getContents(Source_ContentReceiver receiver) {
449 _proxy.getContents(receiver);
450 }
451
452 String get encoding => _proxy.encoding;
453
454 String get fullName => _proxy.fullName;
455
456 int get modificationStamp => _proxy.modificationStamp;
457
458 String get shortName => _proxy.shortName;
459
460 UriKind get uriKind => _proxy.uriKind;
461
462 bool get isInSystemLibrary => _proxy.isInSystemLibrary;
463 }
464
465 /// Get an asset ID for a URL relative to another source asset.
466 AssetId _resolve(AssetId source, String url, TransformLogger logger,
467 Span span) {
468 if (url == null || url == '') return null;
469 var urlBuilder = path.url;
470 var uri = Uri.parse(url);
471
472 if (uri.scheme == 'package') {
473 var segments = new List.from(uri.pathSegments);
474 var package = segments[0];
475 segments[0] = 'lib';
476 return new AssetId(package, segments.join(urlBuilder.separator));
477 }
478 // Dart SDK libraries do not have assets.
479 if (uri.scheme == 'dart') return null;
480
481 if (uri.host != '' || uri.scheme != '' || urlBuilder.isAbsolute(url)) {
482 logger.error('absolute paths not allowed: "$url"', span: span);
483 return null;
484 }
485
486 var targetPath = urlBuilder.normalize(
487 urlBuilder.join(urlBuilder.dirname(source.path), url));
488 return new AssetId(source.package, targetPath);
489 }
490
491
492 /// A completer that waits until all added [Future]s complete.
493 // TODO(blois): Copied from quiver. Remove from here when it gets
494 // added to dart:core. (See #6626.)
495 class FutureGroup<E> {
496 static const _FINISHED = -1;
497
498 int _pending = 0;
499 Future _failedTask;
500 final Completer<List> _completer = new Completer<List>();
501 final List results = [];
502
503 /** Gets the task that failed, if any. */
504 Future get failedTask => _failedTask;
505
506 /**
507 * Wait for [task] to complete.
508 *
509 * If this group has already been marked as completed, a [StateError] will be
510 * thrown.
511 *
512 * If this group has a [failedTask], new tasks will be ignored, because the
513 * error has already been signaled.
514 */
515 void add(Future task) {
516 if (_failedTask != null) return;
517 if (_pending == _FINISHED) throw new StateError("Future already completed");
518
519 _pending++;
520 var i = results.length;
521 results.add(null);
522 task.then((res) {
523 results[i] = res;
524 if (_failedTask != null) return;
525 _pending--;
526 if (_pending == 0) {
527 _pending = _FINISHED;
528 _completer.complete(results);
529 }
530 }, onError: (e, s) {
531 if (_failedTask != null) return;
532 _failedTask = task;
533 _completer.completeError(e, s);
534 });
535 }
536
537 /**
538 * A Future that complets with a List of the values from all the added
539 * tasks, when they have all completed.
540 *
541 * If any task fails, this Future will receive the error. Only the first
542 * error will be sent to the Future.
543 */
544 Future<List<E>> get future => _completer.future;
545 }
OLDNEW
« no previous file with comments | « pkg/code_transformers/lib/src/resolver.dart ('k') | pkg/code_transformers/lib/src/resolver_transformer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698