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

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 16 matching lines...) Expand all
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 <AssetId, _AssetBasedSource>{}; 35 <AssetId, _AssetBasedSource>{};
36 36
37 /// The Dart entry point file where parsing begins.
38 final AssetId entryPoint;
39
40 final AnalysisContext _context = 37 final AnalysisContext _context =
41 AnalysisEngine.instance.createAnalysisContext(); 38 AnalysisEngine.instance.createAnalysisContext();
42 39
43 /// Transform for which this is currently updating, or null when not updating. 40 /// Transform for which this is currently updating, or null when not updating.
44 Transform _currentTransform; 41 Transform _currentTransform;
45 42
46 /// The currently resolved library, or null if unresolved. 43 /// The currently resolved entry libraries, or null if nothing is resolved.
47 LibraryElement _entryLibrary; 44 List<LibraryElement> _entryLibraries;
48 45
49 /// Future indicating when this resolver is done in the current phase. 46 /// Future indicating when this resolver is done in the current phase.
50 Future _lastPhaseComplete = new Future.value(); 47 Future _lastPhaseComplete = new Future.value();
51 48
52 /// Completer for wrapping up the current phase. 49 /// Completer for wrapping up the current phase.
53 Completer _currentPhaseComplete; 50 Completer _currentPhaseComplete;
54 51
55 /// Handler for all Dart SDK (dart:) sources. 52 /// Handler for all Dart SDK (dart:) sources.
56 DirectoryBasedDartSdk _dartSdk; 53 DirectoryBasedDartSdk _dartSdk;
57 54
58 /// Creates a resolver that will resolve the Dart code starting at 55 /// Creates a resolver, where [sdkDir] is the root directory of the Dart SDK,
59 /// [entryPoint]. 56 /// for resolving `dart:*` imports.
60 /// 57 ResolverImpl(String sdkDir, {AnalysisOptions options}) {
61 /// [sdkDir] is the root directory of the Dart SDK, for resolving dart:
62 /// imports.
63 ResolverImpl(this.entryPoint, String sdkDir, {AnalysisOptions options}) {
64 if (options == null) { 58 if (options == null) {
65 options = new AnalysisOptionsImpl() 59 options = new AnalysisOptionsImpl()
66 ..cacheSize = 256 // # of sources to cache ASTs for. 60 ..cacheSize = 256 // # of sources to cache ASTs for.
67 ..preserveComments = false 61 ..preserveComments = false
68 ..analyzeFunctionBodies = true; 62 ..analyzeFunctionBodies = true;
69 } 63 }
70 _context.analysisOptions = options; 64 _context.analysisOptions = options;
71 65
72 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir)); 66 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir));
73 _dartSdk.context.analysisOptions = options; 67 _dartSdk.context.analysisOptions = options;
74 68
75 _context.sourceFactory = new SourceFactory([ 69 _context.sourceFactory = new SourceFactory([
76 new DartUriResolverProxy(_dartSdk), 70 new DartUriResolverProxy(_dartSdk),
77 new _AssetUriResolver(this)]); 71 new _AssetUriResolver(this)]);
78 } 72 }
79 73
80 LibraryElement get entryLibrary => _entryLibrary; 74 LibraryElement getLibrary(AssetId assetId) {
75 var source = sources[assetId];
76 if (source._libraryElement == null) {
77 source._libraryElement = _context.computeLibraryElement(source);
blois 2014/03/17 16:55:45 computeLibraryElement is long-running the first ti
Siggi Cherem (dart-lang) 2014/03/17 20:13:39 Good to know, I had assumed that it didn't cache i
78 }
79 return source == null ? null : source._libraryElement;
blois 2014/03/17 16:55:45 If source can be null then it will crash in the if
Siggi Cherem (dart-lang) 2014/03/17 20:13:39 good catch! this was a last minute refactor and I
80 }
81 81
82 Future<Resolver> resolve(Transform transform) { 82 Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) {
83 // Can only have one resolve in progress at a time, so chain the current 83 // Can only have one resolve in progress at a time, so chain the current
84 // resolution to be after the last one. 84 // resolution to be after the last one.
85 var phaseComplete = new Completer(); 85 var phaseComplete = new Completer();
86 var future = _lastPhaseComplete.then((_) { 86 var future = _lastPhaseComplete.then((_) {
87 _currentPhaseComplete = phaseComplete; 87 _currentPhaseComplete = phaseComplete;
88 88 return _performResolve(transform,
89 return _performResolve(transform); 89 entryPoints == null ? [transform.primaryInput.id] : entryPoints);
90 }).then((_) => this); 90 }).then((_) => this);
91 // Advance the lastPhaseComplete to be done when this phase is all done. 91 // Advance the lastPhaseComplete to be done when this phase is all done.
92 _lastPhaseComplete = phaseComplete.future; 92 _lastPhaseComplete = phaseComplete.future;
93 return future; 93 return future;
94 } 94 }
95 95
96 void release() { 96 void release() {
97 if (_currentPhaseComplete == null) { 97 if (_currentPhaseComplete == null) {
98 throw new StateError('Releasing without current lock.'); 98 throw new StateError('Releasing without current lock.');
99 } 99 }
100 _currentPhaseComplete.complete(null); 100 _currentPhaseComplete.complete(null);
101 _currentPhaseComplete = null; 101 _currentPhaseComplete = null;
102 102
103 // Clear out the entry lib since it should not be referenced after release. 103 // Clear out libraries since they should not be referenced after release.
104 _entryLibrary = null; 104 sources.values.forEach((source) { source._libraryElement = null; });
105 _entryLibraries = null;
106 _currentTransform = null;
105 } 107 }
106 108
107 Future _performResolve(Transform transform) { 109 Future _performResolve(Transform transform, List<AssetId> entryPoints) {
108 if (_currentTransform != null) { 110 if (_currentTransform != null) {
109 throw new StateError('Cannot be accessed by concurrent transforms'); 111 throw new StateError('Cannot be accessed by concurrent transforms');
110 } 112 }
111 _currentTransform = transform; 113 _currentTransform = transform;
112 114
113 // Basic approach is to start at the first file, update it's contents 115 // Basic approach is to start at the first file, update it's contents
114 // and see if it changed, then walk all files accessed by it. 116 // and see if it changed, then walk all files accessed by it.
115 var visited = new Set<AssetId>(); 117 var visited = new Set<AssetId>();
116 var visiting = new FutureGroup(); 118 var visiting = new FutureGroup();
117 var toUpdate = []; 119 var toUpdate = [];
118 120
119 void processAsset(AssetId assetId) { 121 void processAsset(AssetId assetId) {
120 visited.add(assetId); 122 visited.add(assetId);
121 123
122 visiting.add(transform.readInputAsString(assetId).then((contents) { 124 visiting.add(transform.readInputAsString(assetId).then((contents) {
123 var source = sources[assetId]; 125 var source = sources[assetId];
124 if (source == null) { 126 if (source == null) {
125 source = new _AssetBasedSource(assetId, this); 127 source = new _AssetBasedSource(assetId, this);
126 sources[assetId] = source; 128 sources[assetId] = source;
127 } 129 }
128 source.updateDependencies(contents); 130 source.updateDependencies(contents);
129 toUpdate.add(new _PendingUpdate(source, contents)); 131 toUpdate.add(new _PendingUpdate(source, contents));
130 source.dependentAssets.where((id) => !visited.contains(id)) 132 source.dependentAssets.where((id) => !visited.contains(id))
131 .forEach(processAsset); 133 .forEach(processAsset);
132 }, onError: (e) { 134 }, onError: (e) {
133 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId])); 135 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId]));
134 sources.remove(assetId); 136 sources.remove(assetId);
135 })); 137 }));
136 } 138 }
137 processAsset(entryPoint); 139 entryPoints.forEach(processAsset);
138 140
139 // Once we have all asset sources updated with the new contents then 141 // Once we have all asset sources updated with the new contents then
140 // resolve everything. 142 // resolve everything.
141 return visiting.future.then((_) { 143 return visiting.future.then((_) {
142 var changeSet = new ChangeSet(); 144 var changeSet = new ChangeSet();
143 toUpdate.forEach((pending) => pending.apply(changeSet)); 145 toUpdate.forEach((pending) => pending.apply(changeSet));
144 var unreachableAssets = new Set.from(sources.keys).difference(visited); 146 var unreachableAssets = new Set.from(sources.keys).difference(visited);
145 for (var unreachable in unreachableAssets) { 147 for (var unreachable in unreachableAssets) {
146 changeSet.removedSource(sources[unreachable]); 148 changeSet.removedSource(sources[unreachable]);
147 sources.remove(unreachable); 149 sources.remove(unreachable);
148 } 150 }
149 151
150 // Update the analyzer context with the latest sources 152 // Update the analyzer context with the latest sources
151 _context.applyChanges(changeSet); 153 _context.applyChanges(changeSet);
152 // Resolve the AST 154 // Force resolve each entry point (the getter will ensure the library is
153 _entryLibrary = _context.computeLibraryElement(sources[entryPoint]); 155 // computed first).
154 _currentTransform = null; 156 _entryLibraries = [];
blois 2014/03/17 16:55:45 _entryLibraries = entryPoints.map((id) { var sou
Siggi Cherem (dart-lang) 2014/03/17 20:13:39 Done.
157 for (var id in entryPoints) {
158 var source = sources[id];
159 source._libraryElement = _context.computeLibraryElement(source);
160 _entryLibraries.add(source._libraryElement);
161 }
155 }); 162 });
156 } 163 }
157 164
158 Iterable<LibraryElement> get libraries => entryLibrary.visibleLibraries; 165 Iterable<LibraryElement> get libraries {
166 var all = new Set();
blois 2014/03/17 16:55:45 => _entryLibraries.expand((lib) => lib.visibleLibr
Siggi Cherem (dart-lang) 2014/03/17 20:13:39 Done.
167 for (var lib in _entryLibraries) {
168 all.addAll(lib.visibleLibraries);
169 }
170 return all;
171 }
159 172
160 LibraryElement getLibraryByName(String libraryName) => 173 LibraryElement getLibraryByName(String libraryName) =>
161 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null); 174 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
162 175
163 LibraryElement getLibraryByUri(Uri uri) => 176 LibraryElement getLibraryByUri(Uri uri) =>
164 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null); 177 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
165 178
166 ClassElement getType(String typeName) { 179 ClassElement getType(String typeName) {
167 var dotIndex = typeName.lastIndexOf('.'); 180 var dotIndex = typeName.lastIndexOf('.');
168 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex); 181 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); 235 throw new StateError('Unable to resolve URI for ${source.runtimeType}');
223 } 236 }
224 237
225 AssetId getSourceAssetId(Element element) { 238 AssetId getSourceAssetId(Element element) {
226 var source = element.source; 239 var source = element.source;
227 if (source is _AssetBasedSource) return source.assetId; 240 if (source is _AssetBasedSource) return source.assetId;
228 return null; 241 return null;
229 } 242 }
230 243
231 Span getSourceSpan(Element element) { 244 Span getSourceSpan(Element element) {
232 var sourceFile = _getSourceFile(element); 245 var sourceFile = getSourceFile(element);
233 if (sourceFile == null) return null; 246 if (sourceFile == null) return null;
234 return sourceFile.span(element.node.offset, element.node.end); 247 return sourceFile.span(element.node.offset, element.node.end);
235 } 248 }
236 249
237 TextEditTransaction createTextEditTransaction(Element element) { 250 TextEditTransaction createTextEditTransaction(Element element) {
238 if (element.source is! _AssetBasedSource) return null; 251 if (element.source is! _AssetBasedSource) return null;
239 252
253 // Cannot edit unless there is an active transformer.
254 if (_currentTransform == null) return null;
255
240 _AssetBasedSource source = element.source; 256 _AssetBasedSource source = element.source;
241 // Cannot modify assets in other packages. 257 // Cannot modify assets in other packages.
242 if (source.assetId.package != entryPoint.package) return null; 258 if (source.assetId.package != _currentTransform.primaryInput.id.package) {
259 return null;
260 }
243 261
244 var sourceFile = _getSourceFile(element); 262 var sourceFile = getSourceFile(element);
245 if (sourceFile == null) return null; 263 if (sourceFile == null) return null;
246 264
247 return new TextEditTransaction(source.rawContents, sourceFile); 265 return new TextEditTransaction(source.rawContents, sourceFile);
248 } 266 }
249 267
250 /// Gets the SourceFile for the source of the element. 268 /// Gets the SourceFile for the source of the element.
251 SourceFile _getSourceFile(Element element) { 269 SourceFile getSourceFile(Element element) {
252 var assetId = getSourceAssetId(element); 270 var assetId = getSourceAssetId(element);
253 if (assetId == null) return null; 271 if (assetId == null) return null;
254 272
255 var importUri = _getSourceUri(element, from: entryPoint); 273 var importUri = _getSourceUri(element);
256 var spanPath = importUri != null ? importUri.toString() : assetId.path; 274 var spanPath = importUri != null ? importUri.toString() : assetId.path;
257 return new SourceFile.text(spanPath, sources[assetId].rawContents); 275 return new SourceFile.text(spanPath, sources[assetId].rawContents);
258 } 276 }
259 } 277 }
260 278
261 /// Implementation of Analyzer's Source for Barback based assets. 279 /// Implementation of Analyzer's Source for Barback based assets.
262 class _AssetBasedSource extends Source { 280 class _AssetBasedSource extends Source {
263 281
264 /// Asset ID where this source can be found. 282 /// Asset ID where this source can be found.
265 final AssetId assetId; 283 final AssetId assetId;
266 284
267 /// The resolver this is being used in. 285 /// The resolver this is being used in.
268 final ResolverImpl _resolver; 286 final ResolverImpl _resolver;
269 287
270 /// Cache of dependent asset IDs, to avoid re-parsing the AST. 288 /// Cache of dependent asset IDs, to avoid re-parsing the AST.
271 Iterable<AssetId> _dependentAssets; 289 Iterable<AssetId> _dependentAssets;
272 290
273 /// The current revision of the file, incremented only when file changes. 291 /// The current revision of the file, incremented only when file changes.
274 int _revision = 0; 292 int _revision = 0;
275 293
276 /// The file contents. 294 /// The file contents.
277 String _contents; 295 String _contents;
278 296
297 LibraryElement _libraryElement;
298
279 _AssetBasedSource(this.assetId, this._resolver); 299 _AssetBasedSource(this.assetId, this._resolver);
280 300
281 /// Update the dependencies of this source. This parses [contents] but avoids 301 /// Update the dependencies of this source. This parses [contents] but avoids
282 /// any analyzer resolution. 302 /// any analyzer resolution.
283 void updateDependencies(String contents) { 303 void updateDependencies(String contents) {
284 if (contents == _contents) return; 304 if (contents == _contents) return;
285 var unit = _parseCompilationUnit(contents); 305 var unit = _parseCompilationUnit(contents);
286 _dependentAssets = unit.directives 306 _dependentAssets = unit.directives
287 .where((d) => (d is ImportDirective || d is PartDirective || 307 .where((d) => (d is ImportDirective || d is PartDirective ||
288 d is ExportDirective)) 308 d is ExportDirective))
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 _logger.error('Could not load asset $id'); 373 _logger.error('Could not load asset $id');
354 } 374 }
355 return source; 375 return source;
356 } 376 }
357 377
358 /// For logging errors. 378 /// For logging errors.
359 Span _getSpan(AstNode node, [String contents]) => 379 Span _getSpan(AstNode node, [String contents]) =>
360 _getSourceFile(contents).span(node.offset, node.end); 380 _getSourceFile(contents).span(node.offset, node.end);
361 /// For logging errors. 381 /// For logging errors.
362 SourceFile _getSourceFile([String contents]) { 382 SourceFile _getSourceFile([String contents]) {
363 var uri = getSourceUri(_resolver.entryPoint); 383 var uri = getSourceUri();
364 var path = uri != null ? uri.toString() : assetId.path; 384 var path = uri != null ? uri.toString() : assetId.path;
365 return new SourceFile.text(path, contents != null ? contents : rawContents); 385 return new SourceFile.text(path, contents != null ? contents : rawContents);
366 } 386 }
367 387
368 /// Gets a URI which would be appropriate for importing this file. 388 /// Gets a URI which would be appropriate for importing this file.
369 /// 389 ///
370 /// Note that this file may represent a non-importable file such as a part. 390 /// Note that this file may represent a non-importable file such as a part.
371 Uri getSourceUri([AssetId from]) { 391 Uri getSourceUri([AssetId from]) {
372 if (!assetId.path.startsWith('lib/')) { 392 if (!assetId.path.startsWith('lib/')) {
373 // Cannot do absolute imports of non lib-based assets. 393 // Cannot do absolute imports of non lib-based assets.
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 622
603 void apply(ChangeSet changeSet) { 623 void apply(ChangeSet changeSet) {
604 if (!source.updateContents(content)) return; 624 if (!source.updateContents(content)) return;
605 if (source._revision == 1 && source._contents != null) { 625 if (source._revision == 1 && source._contents != null) {
606 changeSet.addedSource(source); 626 changeSet.addedSource(source);
607 } else { 627 } else {
608 changeSet.changedSource(source); 628 changeSet.changedSource(source);
609 } 629 }
610 } 630 }
611 } 631 }
OLDNEW
« no previous file with comments | « pkg/code_transformers/lib/src/resolver.dart ('k') | pkg/code_transformers/lib/src/resolvers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698