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

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/analyzer.dart' show parseCompilationUnit; 8 import 'package:analyzer/analyzer.dart' show parseCompilationUnit;
9 import 'package:analyzer/src/generated/ast.dart'; 9 import 'package:analyzer/src/generated/ast.dart';
10 import 'package:analyzer/src/generated/element.dart'; 10 import 'package:analyzer/src/generated/element.dart';
(...skipping 17 matching lines...) Expand all
28 28
29 /// Resolves and updates an AST based on Barback-based assets. 29 /// Resolves and updates an AST based on Barback-based assets.
30 /// 30 ///
31 /// This also provides a handful of useful APIs for traversing and working 31 /// This also provides a handful of useful APIs for traversing and working
32 /// with the resolved AST. 32 /// with the resolved AST.
33 class ResolverImpl implements Resolver { 33 class ResolverImpl implements Resolver {
34 /// Cache of all asset sources currently referenced. 34 /// Cache of all asset sources currently referenced.
35 final Map<AssetId, _AssetBasedSource> sources = 35 final Map<AssetId, _AssetBasedSource> sources =
36 <AssetId, _AssetBasedSource>{}; 36 <AssetId, _AssetBasedSource>{};
37 37
38 /// The Dart entry point file where parsing begins.
39 final AssetId entryPoint;
40
41 final AnalysisContext _context = 38 final AnalysisContext _context =
42 AnalysisEngine.instance.createAnalysisContext(); 39 AnalysisEngine.instance.createAnalysisContext();
43 40
44 /// Transform for which this is currently updating, or null when not updating. 41 /// Transform for which this is currently updating, or null when not updating.
45 Transform _currentTransform; 42 Transform _currentTransform;
46 43
47 /// The currently resolved library, or null if unresolved. 44 /// The currently resolved entry libraries, or null if nothing is resolved.
48 LibraryElement _entryLibrary; 45 List<LibraryElement> _entryLibraries;
49 46
50 /// Future indicating when this resolver is done in the current phase. 47 /// Future indicating when this resolver is done in the current phase.
51 Future _lastPhaseComplete = new Future.value(); 48 Future _lastPhaseComplete = new Future.value();
52 49
53 /// Completer for wrapping up the current phase. 50 /// Completer for wrapping up the current phase.
54 Completer _currentPhaseComplete; 51 Completer _currentPhaseComplete;
55 52
56 /// Handler for all Dart SDK (dart:) sources. 53 /// Handler for all Dart SDK (dart:) sources.
57 DirectoryBasedDartSdk _dartSdk; 54 DirectoryBasedDartSdk _dartSdk;
58 55
59 /// Creates a resolver that will resolve the Dart code starting at 56 /// Creates a resolver, where [sdkDir] is the root directory of the Dart SDK,
60 /// [entryPoint]. 57 /// for resolving `dart:*` imports.
61 /// 58 ResolverImpl(String sdkDir, {AnalysisOptions options}) {
62 /// [sdkDir] is the root directory of the Dart SDK, for resolving dart:
63 /// imports.
64 ResolverImpl(this.entryPoint, String sdkDir, {AnalysisOptions options}) {
65 if (options == null) { 59 if (options == null) {
66 options = new AnalysisOptionsImpl() 60 options = new AnalysisOptionsImpl()
67 ..cacheSize = 256 // # of sources to cache ASTs for. 61 ..cacheSize = 256 // # of sources to cache ASTs for.
68 ..preserveComments = false 62 ..preserveComments = false
69 ..analyzeFunctionBodies = true; 63 ..analyzeFunctionBodies = true;
70 } 64 }
71 _context.analysisOptions = options; 65 _context.analysisOptions = options;
72 66
73 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir)); 67 _dartSdk = new _DirectoryBasedDartSdkProxy(new JavaFile(sdkDir));
74 _dartSdk.context.analysisOptions = options; 68 _dartSdk.context.analysisOptions = options;
75 69
76 _context.sourceFactory = new SourceFactory([ 70 _context.sourceFactory = new SourceFactory([
77 new DartUriResolverProxy(_dartSdk), 71 new DartUriResolverProxy(_dartSdk),
78 new _AssetUriResolver(this)]); 72 new _AssetUriResolver(this)]);
79 } 73 }
80 74
81 LibraryElement get entryLibrary => _entryLibrary; 75 LibraryElement getLibrary(AssetId assetId) {
76 var source = sources[assetId];
77 return source == null ? null : _context.computeLibraryElement(source);
78 }
82 79
83 Future<Resolver> resolve(Transform transform) { 80 Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) {
84 // Can only have one resolve in progress at a time, so chain the current 81 // Can only have one resolve in progress at a time, so chain the current
85 // resolution to be after the last one. 82 // resolution to be after the last one.
86 var phaseComplete = new Completer(); 83 var phaseComplete = new Completer();
87 var future = _lastPhaseComplete.then((_) { 84 var future = _lastPhaseComplete.then((_) {
88 _currentPhaseComplete = phaseComplete; 85 _currentPhaseComplete = phaseComplete;
89 86 return _performResolve(transform,
90 return _performResolve(transform); 87 entryPoints == null ? [transform.primaryInput.id] : entryPoints);
91 }).then((_) => this); 88 }).then((_) => this);
92 // Advance the lastPhaseComplete to be done when this phase is all done. 89 // Advance the lastPhaseComplete to be done when this phase is all done.
93 _lastPhaseComplete = phaseComplete.future; 90 _lastPhaseComplete = phaseComplete.future;
94 return future; 91 return future;
95 } 92 }
96 93
97 void release() { 94 void release() {
98 if (_currentPhaseComplete == null) { 95 if (_currentPhaseComplete == null) {
99 throw new StateError('Releasing without current lock.'); 96 throw new StateError('Releasing without current lock.');
100 } 97 }
101 _currentPhaseComplete.complete(null); 98 _currentPhaseComplete.complete(null);
102 _currentPhaseComplete = null; 99 _currentPhaseComplete = null;
103 100
104 // Clear out the entry lib since it should not be referenced after release. 101 // Clear out libraries since they should not be referenced after release.
105 _entryLibrary = null; 102 _entryLibraries = null;
103 _currentTransform = null;
106 } 104 }
107 105
108 Future _performResolve(Transform transform) { 106 Future _performResolve(Transform transform, List<AssetId> entryPoints) {
109 if (_currentTransform != null) { 107 if (_currentTransform != null) {
110 throw new StateError('Cannot be accessed by concurrent transforms'); 108 throw new StateError('Cannot be accessed by concurrent transforms');
111 } 109 }
112 _currentTransform = transform; 110 _currentTransform = transform;
113 111
114 // Basic approach is to start at the first file, update it's contents 112 // Basic approach is to start at the first file, update it's contents
115 // and see if it changed, then walk all files accessed by it. 113 // and see if it changed, then walk all files accessed by it.
116 var visited = new Set<AssetId>(); 114 var visited = new Set<AssetId>();
117 var visiting = new FutureGroup(); 115 var visiting = new FutureGroup();
118 var toUpdate = []; 116 var toUpdate = [];
119 117
120 void processAsset(AssetId assetId) { 118 void processAsset(AssetId assetId) {
121 visited.add(assetId); 119 visited.add(assetId);
122 120
123 visiting.add(transform.readInputAsString(assetId).then((contents) { 121 visiting.add(transform.readInputAsString(assetId).then((contents) {
124 var source = sources[assetId]; 122 var source = sources[assetId];
125 if (source == null) { 123 if (source == null) {
126 source = new _AssetBasedSource(assetId, this); 124 source = new _AssetBasedSource(assetId, this);
127 sources[assetId] = source; 125 sources[assetId] = source;
128 } 126 }
129 source.updateDependencies(contents); 127 source.updateDependencies(contents);
130 toUpdate.add(new _PendingUpdate(source, contents)); 128 toUpdate.add(new _PendingUpdate(source, contents));
131 source.dependentAssets.where((id) => !visited.contains(id)) 129 source.dependentAssets.where((id) => !visited.contains(id))
132 .forEach(processAsset); 130 .forEach(processAsset);
133 }, onError: (e) { 131 }, onError: (e) {
134 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId])); 132 _context.applyChanges(new ChangeSet()..removedSource(sources[assetId]));
135 sources.remove(assetId); 133 sources.remove(assetId);
136 })); 134 }));
137 } 135 }
138 processAsset(entryPoint); 136 entryPoints.forEach(processAsset);
139 137
140 // Once we have all asset sources updated with the new contents then 138 // Once we have all asset sources updated with the new contents then
141 // resolve everything. 139 // resolve everything.
142 return visiting.future.then((_) { 140 return visiting.future.then((_) {
143 var changeSet = new ChangeSet(); 141 var changeSet = new ChangeSet();
144 toUpdate.forEach((pending) => pending.apply(changeSet)); 142 toUpdate.forEach((pending) => pending.apply(changeSet));
145 var unreachableAssets = new Set.from(sources.keys).difference(visited); 143 var unreachableAssets = new Set.from(sources.keys).difference(visited);
146 for (var unreachable in unreachableAssets) { 144 for (var unreachable in unreachableAssets) {
147 changeSet.removedSource(sources[unreachable]); 145 changeSet.removedSource(sources[unreachable]);
148 sources.remove(unreachable); 146 sources.remove(unreachable);
149 } 147 }
150 148
151 // Update the analyzer context with the latest sources 149 // Update the analyzer context with the latest sources
152 _context.applyChanges(changeSet); 150 _context.applyChanges(changeSet);
153 // Resolve the AST 151 // Force resolve each entry point (the getter will ensure the library is
154 _entryLibrary = _context.computeLibraryElement(sources[entryPoint]); 152 // computed first).
155 _currentTransform = null; 153 _entryLibraries = entryPoints
154 .map((id) => _context.computeLibraryElement(sources[id])).toList();
156 }); 155 });
157 } 156 }
158 157
159 Iterable<LibraryElement> get libraries => entryLibrary.visibleLibraries; 158 Iterable<LibraryElement> get libraries =>
159 _entryLibraries.expand((lib) => lib.visibleLibraries).toSet();
160 160
161 LibraryElement getLibraryByName(String libraryName) => 161 LibraryElement getLibraryByName(String libraryName) =>
162 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null); 162 libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
163 163
164 LibraryElement getLibraryByUri(Uri uri) => 164 LibraryElement getLibraryByUri(Uri uri) =>
165 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null); 165 libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
166 166
167 ClassElement getType(String typeName) { 167 ClassElement getType(String typeName) {
168 var dotIndex = typeName.lastIndexOf('.'); 168 var dotIndex = typeName.lastIndexOf('.');
169 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex); 169 var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); 223 throw new StateError('Unable to resolve URI for ${source.runtimeType}');
224 } 224 }
225 225
226 AssetId getSourceAssetId(Element element) { 226 AssetId getSourceAssetId(Element element) {
227 var source = element.source; 227 var source = element.source;
228 if (source is _AssetBasedSource) return source.assetId; 228 if (source is _AssetBasedSource) return source.assetId;
229 return null; 229 return null;
230 } 230 }
231 231
232 Span getSourceSpan(Element element) { 232 Span getSourceSpan(Element element) {
233 var sourceFile = _getSourceFile(element); 233 var sourceFile = getSourceFile(element);
234 if (sourceFile == null) return null; 234 if (sourceFile == null) return null;
235 return sourceFile.span(element.node.offset, element.node.end); 235 return sourceFile.span(element.node.offset, element.node.end);
236 } 236 }
237 237
238 TextEditTransaction createTextEditTransaction(Element element) { 238 TextEditTransaction createTextEditTransaction(Element element) {
239 if (element.source is! _AssetBasedSource) return null; 239 if (element.source is! _AssetBasedSource) return null;
240 240
241 // Cannot edit unless there is an active transformer.
242 if (_currentTransform == null) return null;
243
241 _AssetBasedSource source = element.source; 244 _AssetBasedSource source = element.source;
242 // Cannot modify assets in other packages. 245 // Cannot modify assets in other packages.
243 if (source.assetId.package != entryPoint.package) return null; 246 if (source.assetId.package != _currentTransform.primaryInput.id.package) {
247 return null;
248 }
244 249
245 var sourceFile = _getSourceFile(element); 250 var sourceFile = getSourceFile(element);
246 if (sourceFile == null) return null; 251 if (sourceFile == null) return null;
247 252
248 return new TextEditTransaction(source.rawContents, sourceFile); 253 return new TextEditTransaction(source.rawContents, sourceFile);
249 } 254 }
250 255
251 /// Gets the SourceFile for the source of the element. 256 /// Gets the SourceFile for the source of the element.
252 SourceFile _getSourceFile(Element element) { 257 SourceFile getSourceFile(Element element) {
253 var assetId = getSourceAssetId(element); 258 var assetId = getSourceAssetId(element);
254 if (assetId == null) return null; 259 if (assetId == null) return null;
255 260
256 var importUri = _getSourceUri(element, from: entryPoint); 261 var importUri = _getSourceUri(element);
257 var spanPath = importUri != null ? importUri.toString() : assetId.path; 262 var spanPath = importUri != null ? importUri.toString() : assetId.path;
258 return new SourceFile.text(spanPath, sources[assetId].rawContents); 263 return new SourceFile.text(spanPath, sources[assetId].rawContents);
259 } 264 }
260 } 265 }
261 266
262 /// Implementation of Analyzer's Source for Barback based assets. 267 /// Implementation of Analyzer's Source for Barback based assets.
263 class _AssetBasedSource extends Source { 268 class _AssetBasedSource extends Source {
264 269
265 /// Asset ID where this source can be found. 270 /// Asset ID where this source can be found.
266 final AssetId assetId; 271 final AssetId assetId;
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 _logger.error('Could not load asset $id'); 359 _logger.error('Could not load asset $id');
355 } 360 }
356 return source; 361 return source;
357 } 362 }
358 363
359 /// For logging errors. 364 /// For logging errors.
360 Span _getSpan(AstNode node, [String contents]) => 365 Span _getSpan(AstNode node, [String contents]) =>
361 _getSourceFile(contents).span(node.offset, node.end); 366 _getSourceFile(contents).span(node.offset, node.end);
362 /// For logging errors. 367 /// For logging errors.
363 SourceFile _getSourceFile([String contents]) { 368 SourceFile _getSourceFile([String contents]) {
364 var uri = getSourceUri(_resolver.entryPoint); 369 var uri = getSourceUri();
365 var path = uri != null ? uri.toString() : assetId.path; 370 var path = uri != null ? uri.toString() : assetId.path;
366 return new SourceFile.text(path, contents != null ? contents : rawContents); 371 return new SourceFile.text(path, contents != null ? contents : rawContents);
367 } 372 }
368 373
369 /// Gets a URI which would be appropriate for importing this file. 374 /// Gets a URI which would be appropriate for importing this file.
370 /// 375 ///
371 /// Note that this file may represent a non-importable file such as a part. 376 /// Note that this file may represent a non-importable file such as a part.
372 Uri getSourceUri([AssetId from]) { 377 Uri getSourceUri([AssetId from]) {
373 if (!assetId.path.startsWith('lib/')) { 378 if (!assetId.path.startsWith('lib/')) {
374 // Cannot do absolute imports of non lib-based assets. 379 // Cannot do absolute imports of non lib-based assets.
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
587 592
588 void apply(ChangeSet changeSet) { 593 void apply(ChangeSet changeSet) {
589 if (!source.updateContents(content)) return; 594 if (!source.updateContents(content)) return;
590 if (source._revision == 1 && source._contents != null) { 595 if (source._revision == 1 && source._contents != null) {
591 changeSet.addedSource(source); 596 changeSet.addedSource(source);
592 } else { 597 } else {
593 changeSet.changedSource(source); 598 changeSet.changedSource(source);
594 } 599 }
595 } 600 }
596 } 601 }
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