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

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

Issue 200543006: Allow multiple-entry libraries in code_transformers (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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
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/src/generated/ast.dart'; 8 import 'package:analyzer/src/generated/ast.dart';
9 import 'package:analyzer/src/generated/element.dart'; 9 import 'package:analyzer/src/generated/element.dart';
10 import 'package:analyzer/src/generated/engine.dart'; 10 import 'package:analyzer/src/generated/engine.dart';
(...skipping 13 matching lines...) Expand all
24 24
25 /// Resolves and updates an AST based on Barback-based assets. 25 /// Resolves and updates an AST based on Barback-based assets.
26 /// 26 ///
27 /// This also provides a handful of useful APIs for traversing and working 27 /// This also provides a handful of useful APIs for traversing and working
28 /// with the resolved AST. 28 /// with the resolved AST.
29 class ResolverImpl implements Resolver { 29 class ResolverImpl implements Resolver {
30 /// Cache of all asset sources currently referenced. 30 /// Cache of all asset sources currently referenced.
31 final Map<AssetId, _AssetBasedSource> sources = 31 final Map<AssetId, _AssetBasedSource> sources =
32 <AssetId, _AssetBasedSource>{}; 32 <AssetId, _AssetBasedSource>{};
33 33
34 /// The Dart entry point file where parsing begins.
35 final AssetId entryPoint;
36
37 final AnalysisContext _context = 34 final AnalysisContext _context =
38 AnalysisEngine.instance.createAnalysisContext(); 35 AnalysisEngine.instance.createAnalysisContext();
39 36
40 /// Transform for which this is currently updating, or null when not updating. 37 /// Transform for which this is currently updating, or null when not updating.
41 Transform _currentTransform; 38 Transform _currentTransform;
42 39
43 /// The currently resolved library, or null if unresolved.
44 LibraryElement _entryLibrary;
45
46 /// Future indicating when this resolver is done in the current phase. 40 /// Future indicating when this resolver is done in the current phase.
47 Future _lastPhaseComplete = new Future.value(); 41 Future _lastPhaseComplete = new Future.value();
48 42
49 /// Completer for wrapping up the current phase. 43 /// Completer for wrapping up the current phase.
50 Completer _currentPhaseComplete; 44 Completer _currentPhaseComplete;
51 45
52 /// Handler for all Dart SDK (dart:) sources. 46 /// Handler for all Dart SDK (dart:) sources.
53 DirectoryBasedDartSdk _dartSdk; 47 DirectoryBasedDartSdk _dartSdk;
54 48
55 /// Creates a resolver that will resolve the Dart code starting at 49 List<LibraryElement> _entryLibraries;
56 /// [entryPoint]. 50
57 /// 51 /// Creates a resolver, where [sdkDir] is the root directory of the Dart SDK,
58 /// [sdkDir] is the root directory of the Dart SDK, for resolving dart: 52 /// for resolving `dart:*` imports.
59 /// imports. 53 ResolverImpl(String sdkDir, {AnalysisOptions options}) {
60 ResolverImpl(this.entryPoint, String sdkDir, {AnalysisOptions options}) {
61 if (options == null) { 54 if (options == null) {
62 options = new AnalysisOptionsImpl() 55 options = new AnalysisOptionsImpl()
63 ..cacheSize = 256 // # of sources to cache ASTs for. 56 ..cacheSize = 256 // # of sources to cache ASTs for.
64 ..preserveComments = false 57 ..preserveComments = false
65 ..analyzeFunctionBodies = true; 58 ..analyzeFunctionBodies = true;
66 } 59 }
67 _context.analysisOptions = options; 60 _context.analysisOptions = options;
68 61
69 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir)); 62 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir));
70 _dartSdk.context.analysisOptions = options; 63 _dartSdk.context.analysisOptions = options;
71 64
72 _context.sourceFactory = new SourceFactory([ 65 _context.sourceFactory = new SourceFactory([
73 new DartUriResolverProxy(_dartSdk), 66 new DartUriResolverProxy(_dartSdk),
74 new _AssetUriResolver(this)]); 67 new _AssetUriResolver(this)]);
75 } 68 }
76 69
77 LibraryElement get entryLibrary => _entryLibrary; 70 LibraryElement getLibrary(AssetId assetId) => sources[assetId].libraryElement;
78 71
79 Future<Resolver> resolve(Transform transform) { 72 Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) {
80 // Can only have one resolve in progress at a time, so chain the current 73 // Can only have one resolve in progress at a time, so chain the current
81 // resolution to be after the last one. 74 // resolution to be after the last one.
82 var phaseComplete = new Completer(); 75 var phaseComplete = new Completer();
83 var future = _lastPhaseComplete.then((_) { 76 var future = _lastPhaseComplete.then((_) {
84 _currentPhaseComplete = phaseComplete; 77 _currentPhaseComplete = phaseComplete;
85 78 return _performResolve(transform,
86 return _performResolve(transform); 79 entryPoints == null ? [transform.primaryInput.id] : entryPoints);
87 }).then((_) => this); 80 }).then((_) => this);
88 // Advance the lastPhaseComplete to be done when this phase is all done. 81 // Advance the lastPhaseComplete to be done when this phase is all done.
89 _lastPhaseComplete = phaseComplete.future; 82 _lastPhaseComplete = phaseComplete.future;
90 return future; 83 return future;
91 } 84 }
92 85
93 void release() { 86 void release() {
94 if (_currentPhaseComplete == null) { 87 if (_currentPhaseComplete == null) {
95 throw new StateError('Releasing without current lock.'); 88 throw new StateError('Releasing without current lock.');
96 } 89 }
97 _currentPhaseComplete.complete(null); 90 _currentPhaseComplete.complete(null);
98 _currentPhaseComplete = null; 91 _currentPhaseComplete = null;
99 92
100 // Clear out the entry lib since it should not be referenced after release. 93 // Clear out libraries since they should not be referenced after release.
101 _entryLibrary = null; 94 sources.values.forEach((source) { source._libraryElement = null; });
102 } 95 }
103 96
104 Future _performResolve(Transform transform) { 97 Future _performResolve(Transform transform, List<AssetId> entryPoints) {
105 if (_currentTransform != null) { 98 if (_currentTransform != null) {
106 throw new StateError('Cannot be accessed by concurrent transforms'); 99 throw new StateError('Cannot be accessed by concurrent transforms');
107 } 100 }
108 _currentTransform = transform; 101 _currentTransform = transform;
109 102
110 // Basic approach is to start at the first file, update it's contents 103 // Basic approach is to start at the first file, update it's contents
111 // and see if it changed, then walk all files accessed by it. 104 // and see if it changed, then walk all files accessed by it.
112 var visited = new Set<AssetId>(); 105 var visited = new Set<AssetId>();
113 var visiting = new FutureGroup(); 106 var visiting = new FutureGroup();
114 107
(...skipping 10 matching lines...) Expand all
125 118
126 source.dependentAssets 119 source.dependentAssets
127 .where((id) => !visited.contains(id)) 120 .where((id) => !visited.contains(id))
128 .forEach(processAsset); 121 .forEach(processAsset);
129 122
130 }, onError: (e) { 123 }, onError: (e) {
131 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId])); 124 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId]));
132 sources.remove(assetId); 125 sources.remove(assetId);
133 })); 126 }));
134 } 127 }
135 processAsset(entryPoint); 128 entryPoints.forEach(processAsset);
136 129
137 // Once we have all asset sources updated with the new contents then 130 // Once we have all asset sources updated with the new contents then
138 // resolve everything. 131 // resolve everything.
139 return visiting.future.then((_) { 132 return visiting.future.then((_) {
140 var changeSet = new ChangeSet(); 133 var changeSet = new ChangeSet();
141 var unreachableAssets = new Set.from(sources.keys).difference(visited); 134 var unreachableAssets = new Set.from(sources.keys).difference(visited);
142 for (var unreachable in unreachableAssets) { 135 for (var unreachable in unreachableAssets) {
143 changeSet.removedSource(sources[unreachable]); 136 changeSet.removedSource(sources[unreachable]);
144 sources.remove(unreachable); 137 sources.remove(unreachable);
145 } 138 }
146 139
147 // Update the analyzer context with the latest sources 140 // Update the analyzer context with the latest sources
148 _context.applyChanges(changeSet); 141 _context.applyChanges(changeSet);
149 // Resolve the AST 142 // Force resolve each entry point (the getter will ensure the library is
150 _entryLibrary = _context.computeLibraryElement(sources[entryPoint]); 143 // computed first).
144 _entryLibraries =
145 entryPoints.map((e) => sources[e].libraryElement).toList();
151 _currentTransform = null; 146 _currentTransform = null;
152 }); 147 });
153 } 148 }
154 149
155 Iterable<LibraryElement> get libraries => entryLibrary.visibleLibraries; 150 Iterable<LibraryElement> get libraries =>
151 _entryLibraries.expand((e) => e.visibleLibraries);
156 152
157 LibraryElement getLibraryByName(String libraryName) => 153 LibraryElement getLibraryByName(String libraryName) =>
158 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null); 154 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
159 155
160 LibraryElement getLibraryByUri(Uri uri) => 156 LibraryElement getLibraryByUri(Uri uri) =>
161 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null); 157 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
162 158
163 ClassElement getType(String typeName) { 159 ClassElement getType(String typeName) {
164 var dotIndex = typeName.lastIndexOf('.'); 160 var dotIndex = typeName.lastIndexOf('.');
165 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex); 161 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); 215 throw new StateError('Unable to resolve URI for ${source.runtimeType}');
220 } 216 }
221 217
222 AssetId getSourceAssetId(Element element) { 218 AssetId getSourceAssetId(Element element) {
223 var source = element.source; 219 var source = element.source;
224 if (source is _AssetBasedSource) return source.assetId; 220 if (source is _AssetBasedSource) return source.assetId;
225 return null; 221 return null;
226 } 222 }
227 223
228 Span getSourceSpan(Element element) { 224 Span getSourceSpan(Element element) {
229 var sourceFile = _getSourceFile(element); 225 var sourceFile = getSourceFile(element);
230 if (sourceFile == null) return null; 226 if (sourceFile == null) return null;
231 return sourceFile.span(element.node.offset, element.node.end); 227 return sourceFile.span(element.node.offset, element.node.end);
232 } 228 }
233 229
234 TextEditTransaction createTextEditTransaction(Element element) { 230 TextEditTransaction createTextEditTransaction(Element element) {
235 if (element.source is! _AssetBasedSource) return null; 231 if (element.source is! _AssetBasedSource) return null;
236 232
233 // Cannot edit unless there is an active transformer.
234 if (_currentTransform == null) return null;
235
237 _AssetBasedSource source = element.source; 236 _AssetBasedSource source = element.source;
238 // Cannot modify assets in other packages. 237 // Cannot modify assets in other packages.
239 if (source.assetId.package != entryPoint.package) return null; 238 if (source.assetId.package != _currentTransform.primaryInput.id.package) {
239 return null;
240 }
240 241
241 var sourceFile = _getSourceFile(element); 242 var sourceFile = getSourceFile(element);
242 if (sourceFile == null) return null; 243 if (sourceFile == null) return null;
243 244
244 return new TextEditTransaction(source.rawContents, sourceFile); 245 return new TextEditTransaction(source.rawContents, sourceFile);
245 } 246 }
246 247
247 /// Gets the SourceFile for the source of the element. 248 /// Gets the SourceFile for the source of the element.
248 SourceFile _getSourceFile(Element element) { 249 SourceFile getSourceFile(Element element) {
249 var assetId = getSourceAssetId(element); 250 var assetId = getSourceAssetId(element);
250 if (assetId == null) return null; 251 if (assetId == null) return null;
251 252
252 var importUri = _getSourceUri(element, from: entryPoint); 253 var importUri = _getSourceUri(element);
253 var spanPath = importUri != null ? importUri.toString() : assetId.path; 254 var spanPath = importUri != null ? importUri.toString() : assetId.path;
254 return new SourceFile.text(spanPath, sources[assetId].rawContents); 255 return new SourceFile.text(spanPath, sources[assetId].rawContents);
255 } 256 }
256 } 257 }
257 258
258 /// Implementation of Analyzer's Source for Barback based assets. 259 /// Implementation of Analyzer's Source for Barback based assets.
259 class _AssetBasedSource extends Source { 260 class _AssetBasedSource extends Source {
260 261
261 /// Asset ID where this source can be found. 262 /// Asset ID where this source can be found.
262 final AssetId assetId; 263 final AssetId assetId;
263 264
264 /// The resolver this is being used in. 265 /// The resolver this is being used in.
265 final ResolverImpl _resolver; 266 final ResolverImpl _resolver;
266 267
267 /// Cache of dependent asset IDs, to avoid re-parsing the AST. 268 /// Cache of dependent asset IDs, to avoid re-parsing the AST.
268 Iterable<AssetId> _dependentAssets; 269 Iterable<AssetId> _dependentAssets;
269 270
270 /// The current revision of the file, incremented only when file changes. 271 /// The current revision of the file, incremented only when file changes.
271 int _revision = 0; 272 int _revision = 0;
272 273
273 /// The file contents. 274 /// The file contents.
274 String _contents; 275 String _contents;
275 276
277 LibraryElement _libraryElement;
278
279 LibraryElement get libraryElement {
280 if (_libraryElement == null) {
281 _libraryElement = _resolver._context.computeLibraryElement(this);
282 }
283 return _libraryElement;
284 }
285
276 _AssetBasedSource(this.assetId, this._resolver); 286 _AssetBasedSource(this.assetId, this._resolver);
277 287
278 /// Update the contents of this file with [contents]. 288 /// Update the contents of this file with [contents].
279 /// 289 ///
280 /// Returns true if the contents of this asset have changed. 290 /// Returns true if the contents of this asset have changed.
281 bool updateContents(String contents) { 291 bool updateContents(String contents) {
282 if (contents == _contents) return false; 292 if (contents == _contents) return false;
283 var added = _contents == null; 293 var added = _contents == null;
284 _contents = contents; 294 _contents = contents;
285 ++_revision; 295 ++_revision;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 if (source == null) { 363 if (source == null) {
354 _logger.error('Could not load asset $id'); 364 _logger.error('Could not load asset $id');
355 } 365 }
356 return source; 366 return source;
357 } 367 }
358 368
359 /// For logging errors. 369 /// For logging errors.
360 Span _getSpan(AstNode node) => _sourceFile.span(node.offset, node.end); 370 Span _getSpan(AstNode node) => _sourceFile.span(node.offset, node.end);
361 /// For logging errors. 371 /// For logging errors.
362 SourceFile get _sourceFile { 372 SourceFile get _sourceFile {
363 var uri = getSourceUri(_resolver.entryPoint); 373 var uri = getSourceUri();
364 var path = uri != null ? uri.toString() : assetId.path; 374 var path = uri != null ? uri.toString() : assetId.path;
365 375
366 return new SourceFile.text(path, rawContents); 376 return new SourceFile.text(path, rawContents);
367 } 377 }
368 378
369 /// Gets a URI which would be appropriate for importing this file. 379 /// Gets a URI which would be appropriate for importing this file.
370 /// 380 ///
371 /// Note that this file may represent a non-importable file such as a part. 381 /// Note that this file may represent a non-importable file such as a part.
372 Uri getSourceUri([AssetId from]) { 382 Uri getSourceUri([AssetId from]) {
373 if (!assetId.path.startsWith('lib/')) { 383 if (!assetId.path.startsWith('lib/')) {
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 577
568 /** 578 /**
569 * A Future that complets with a List of the values from all the added 579 * A Future that complets with a List of the values from all the added
570 * tasks, when they have all completed. 580 * tasks, when they have all completed.
571 * 581 *
572 * If any task fails, this Future will receive the error. Only the first 582 * If any task fails, this Future will receive the error. Only the first
573 * error will be sent to the Future. 583 * error will be sent to the Future.
574 */ 584 */
575 Future<List<E>> get future => _completer.future; 585 Future<List<E>> get future => _completer.future;
576 } 586 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698