OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015, 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 analyzer_cli.src.package_analyzer; | |
6 | |
7 import 'dart:core' hide Resource; | |
8 import 'dart:io' as io; | |
9 | |
10 import 'package:analyzer/dart/element/element.dart'; | |
11 import 'package:analyzer/file_system/file_system.dart'; | |
12 import 'package:analyzer/file_system/physical_file_system.dart'; | |
13 import 'package:analyzer/source/package_map_resolver.dart'; | |
14 import 'package:analyzer/src/context/cache.dart'; | |
15 import 'package:analyzer/src/context/context.dart'; | |
16 import 'package:analyzer/src/generated/engine.dart'; | |
17 import 'package:analyzer/src/generated/error.dart'; | |
18 import 'package:analyzer/src/generated/resolver.dart'; | |
19 import 'package:analyzer/src/generated/sdk_io.dart'; | |
20 import 'package:analyzer/src/generated/source.dart'; | |
21 import 'package:analyzer/src/generated/source_io.dart'; | |
22 import 'package:analyzer/src/summary/format.dart'; | |
23 import 'package:analyzer/src/summary/idl.dart'; | |
24 import 'package:analyzer/src/summary/resynthesize.dart'; | |
25 import 'package:analyzer/src/summary/summarize_elements.dart'; | |
26 import 'package:analyzer/src/summary/summary_sdk.dart'; | |
27 import 'package:analyzer/src/task/dart.dart'; | |
28 import 'package:analyzer/task/dart.dart'; | |
29 import 'package:analyzer/task/model.dart'; | |
30 import 'package:analyzer_cli/src/analyzer_impl.dart'; | |
31 import 'package:analyzer_cli/src/driver.dart'; | |
32 import 'package:analyzer_cli/src/error_formatter.dart'; | |
33 import 'package:analyzer_cli/src/options.dart'; | |
34 import 'package:path/path.dart' as pathos; | |
35 | |
36 /** | |
37 * If [uri] has the `package` scheme in form of `package:pkg/file.dart`, | |
38 * return the `pkg` name. Otherwise return `null`. | |
39 */ | |
40 String getPackageName(Uri uri) { | |
41 if (uri.scheme != 'package') { | |
42 return null; | |
43 } | |
44 String path = uri.path; | |
45 int index = path.indexOf('/'); | |
46 if (index == -1) { | |
47 return null; | |
48 } | |
49 return path.substring(0, index); | |
50 } | |
51 | |
52 /** | |
53 * A concrete resynthesizer that serves summaries from given file paths. | |
54 */ | |
55 class FileBasedSummaryResynthesizer extends SummaryResynthesizer { | |
56 final Map<String, UnlinkedUnit> unlinkedMap = <String, UnlinkedUnit>{}; | |
57 final Map<String, LinkedLibrary> linkedMap = <String, LinkedLibrary>{}; | |
58 | |
59 FileBasedSummaryResynthesizer( | |
60 SummaryResynthesizer parent, | |
61 AnalysisContext context, | |
62 TypeProvider typeProvider, | |
63 SourceFactory sourceFactory, | |
64 bool strongMode, | |
65 List<String> summaryPaths) | |
66 : super(parent, context, typeProvider, sourceFactory, strongMode) { | |
67 summaryPaths.forEach(_fillMaps); | |
68 } | |
69 | |
70 @override | |
71 LinkedLibrary getLinkedSummary(String uri) { | |
72 return linkedMap[uri]; | |
73 } | |
74 | |
75 @override | |
76 UnlinkedUnit getUnlinkedSummary(String uri) { | |
77 return unlinkedMap[uri]; | |
78 } | |
79 | |
80 @override | |
81 bool hasLibrarySummary(String uri) { | |
82 return linkedMap.containsKey(uri); | |
83 } | |
84 | |
85 void _fillMaps(String path) { | |
86 io.File file = new io.File(path); | |
87 List<int> buffer = file.readAsBytesSync(); | |
88 SdkBundle bundle = new SdkBundle.fromBuffer(buffer); | |
89 for (int i = 0; i < bundle.unlinkedUnitUris.length; i++) { | |
90 unlinkedMap[bundle.unlinkedUnitUris[i]] = bundle.unlinkedUnits[i]; | |
91 } | |
92 for (int i = 0; i < bundle.linkedLibraryUris.length; i++) { | |
93 linkedMap[bundle.linkedLibraryUris[i]] = bundle.linkedLibraries[i]; | |
94 } | |
95 } | |
96 } | |
97 | |
98 /** | |
99 * The [ResourceProvider] that provides results from input package summaries. | |
100 */ | |
101 class InputPackagesResultProvider extends ResultProvider { | |
102 final InternalAnalysisContext context; | |
103 final Map<String, String> packageSummaryInputs; | |
104 | |
105 FileBasedSummaryResynthesizer resynthesizer; | |
106 SummaryResultProvider sdkProvider; | |
107 | |
108 InputPackagesResultProvider(this.context, this.packageSummaryInputs) { | |
109 InternalAnalysisContext sdkContext = context.sourceFactory.dartSdk.context; | |
110 sdkProvider = sdkContext.resultProvider; | |
111 // Set the type provider to prevent the context from computing it. | |
112 context.typeProvider = sdkContext.typeProvider; | |
113 // Create a chained resynthesizer. | |
114 resynthesizer = new FileBasedSummaryResynthesizer( | |
115 sdkProvider.resynthesizer, | |
116 context, | |
117 context.typeProvider, | |
118 context.sourceFactory, | |
119 false, | |
120 packageSummaryInputs.values.toList()); | |
121 } | |
122 | |
123 bool compute(CacheEntry entry, ResultDescriptor result) { | |
Paul Berry
2016/02/23 00:59:29
It looks like some of this code is duplicated from
scheglov
2016/02/23 01:15:28
Yes, probably some of it can be shared.
I will add
| |
124 if (sdkProvider.compute(entry, result)) { | |
125 return true; | |
126 } | |
127 AnalysisTarget target = entry.target; | |
128 // Only library results are supported for now. | |
129 if (target is Source) { | |
130 Uri uri = target.uri; | |
131 // We know how to server results to input packages. | |
132 String sourcePackageName = getPackageName(uri); | |
133 if (!packageSummaryInputs.containsKey(sourcePackageName)) { | |
134 return false; | |
135 } | |
136 // Provide known results. | |
137 String uriString = uri.toString(); | |
138 if (result == LIBRARY_ELEMENT1 || | |
139 result == LIBRARY_ELEMENT2 || | |
140 result == LIBRARY_ELEMENT3 || | |
141 result == LIBRARY_ELEMENT4 || | |
142 result == LIBRARY_ELEMENT5 || | |
143 result == LIBRARY_ELEMENT6 || | |
144 result == LIBRARY_ELEMENT7 || | |
145 result == LIBRARY_ELEMENT8 || | |
146 result == LIBRARY_ELEMENT || | |
147 false) { | |
148 LibraryElement libraryElement = | |
149 resynthesizer.getLibraryElement(uriString); | |
150 entry.setValue(result, libraryElement, TargetedResult.EMPTY_LIST); | |
151 return true; | |
152 } else if (result == READY_LIBRARY_ELEMENT2 || | |
153 result == READY_LIBRARY_ELEMENT5 || | |
154 result == READY_LIBRARY_ELEMENT6) { | |
155 entry.setValue(result, true, TargetedResult.EMPTY_LIST); | |
156 return true; | |
157 } else if (result == SOURCE_KIND) { | |
158 if (resynthesizer.linkedMap.containsKey(uriString)) { | |
159 entry.setValue(result, SourceKind.LIBRARY, TargetedResult.EMPTY_LIST); | |
160 return true; | |
161 } | |
162 if (resynthesizer.unlinkedMap.containsKey(uriString)) { | |
163 entry.setValue(result, SourceKind.PART, TargetedResult.EMPTY_LIST); | |
164 return true; | |
165 } | |
166 return false; | |
167 } | |
168 } | |
169 return false; | |
170 } | |
171 } | |
172 | |
173 /** | |
174 * The [UriResolver] that knows about sources that are parts of packages which | |
175 * are served from their summaries. | |
176 */ | |
177 class InSummaryPackageUriResolver extends UriResolver { | |
Brian Wilkerson
2016/02/23 00:27:01
I don't understand the need for either this class
scheglov
2016/02/23 01:15:28
Yes, we did not get to this part offline today.
| |
178 final Map<String, String> packageSummaryInputs; | |
179 | |
180 InSummaryPackageUriResolver(this.packageSummaryInputs); | |
181 | |
182 @override | |
183 Source resolveAbsolute(Uri uri, [Uri actualUri]) { | |
184 actualUri ??= uri; | |
185 String packageName = getPackageName(actualUri); | |
186 if (packageSummaryInputs.containsKey(packageName)) { | |
187 return new InSummarySource(actualUri); | |
188 } | |
189 return null; | |
190 } | |
191 } | |
192 | |
193 /** | |
194 * A placeholder of a source that is part of a package whose analysis results | |
195 * are served from its summary. This source uses its URI as [fullName] and has | |
196 * empty contents. | |
197 */ | |
198 class InSummarySource extends Source { | |
199 final Uri uri; | |
200 | |
201 InSummarySource(this.uri); | |
202 | |
203 @override | |
204 TimestampedData<String> get contents => new TimestampedData<String>(0, ''); | |
205 | |
206 @override | |
207 String get encoding => uri.toString(); | |
208 | |
209 @override | |
210 String get fullName => encoding; | |
211 | |
212 @override | |
213 bool get isInSystemLibrary => false; | |
214 | |
215 @override | |
216 int get modificationStamp => 0; | |
217 | |
218 @override | |
219 String get shortName => pathos.basename(fullName); | |
220 | |
221 @override | |
222 UriKind get uriKind => UriKind.PACKAGE_URI; | |
223 | |
224 @override | |
225 bool exists() => true; | |
226 | |
227 @override | |
228 Uri resolveRelativeUri(Uri relativeUri) { | |
229 Uri baseUri = uri; | |
230 return baseUri.resolveUri(relativeUri); | |
231 } | |
232 | |
233 @override | |
234 String toString() => uri.toString(); | |
235 } | |
236 | |
237 /** | |
238 * The hermetic whole package analyzer. | |
239 */ | |
240 class PackageAnalyzer { | |
241 final CommandLineOptions options; | |
242 | |
243 String packagePath; | |
244 String packageLibPath; | |
245 | |
246 final ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; | |
247 InternalAnalysisContext context; | |
248 final List<Source> explicitSources = <Source>[]; | |
249 | |
250 final List<String> linkedLibraryUris = <String>[]; | |
251 final List<LinkedLibraryBuilder> linkedLibraries = <LinkedLibraryBuilder>[]; | |
252 final List<String> unlinkedUnitUris = <String>[]; | |
253 final List<UnlinkedUnitBuilder> unlinkedUnits = <UnlinkedUnitBuilder>[]; | |
254 | |
255 PackageAnalyzer(this.options); | |
256 | |
257 /** | |
258 * Perform package analysis according to the given [options]. | |
259 */ | |
260 ErrorSeverity analyze() { | |
261 packagePath = options.packageModePath; | |
262 packageLibPath = resourceProvider.pathContext.join(packagePath, 'lib'); | |
263 if (packageLibPath == null) { | |
264 errorSink.writeln('--package-mode-path must be set to the root ' | |
265 'folder of the package to analyze.'); | |
266 io.exitCode = ErrorSeverity.ERROR.ordinal; | |
267 return ErrorSeverity.ERROR; | |
268 } | |
269 | |
270 // Write the progress message. | |
271 if (!options.machineFormat) { | |
272 outSink.writeln("Analyzing sources ${options.sourceFiles}..."); | |
273 } | |
274 | |
275 // Prepare the analysis context. | |
276 _createContext(); | |
277 | |
278 // Add sources. | |
279 ChangeSet changeSet = new ChangeSet(); | |
280 for (String path in options.sourceFiles) { | |
281 if (AnalysisEngine.isDartFileName(path)) { | |
282 path = resourceProvider.pathContext.absolute(path); | |
283 File file = resourceProvider.getFile(path); | |
284 if (!file.exists) { | |
285 errorSink.writeln('File not found: $path'); | |
286 io.exitCode = ErrorSeverity.ERROR.ordinal; | |
287 return ErrorSeverity.ERROR; | |
288 } | |
289 Source source = _createSourceInContext(file); | |
290 explicitSources.add(source); | |
291 changeSet.addedSource(source); | |
292 } | |
293 } | |
294 context.applyChanges(changeSet); | |
295 | |
296 // Perform full analysis. | |
297 while (true) { | |
298 AnalysisResult analysisResult = context.performAnalysisTask(); | |
299 if (!analysisResult.hasMoreWork) { | |
300 break; | |
301 } | |
302 } | |
303 | |
304 // Write summary for Dart libraries. | |
305 if (options.packageSummaryOutput != null) { | |
306 for (Source source in context.librarySources) { | |
307 if (pathos.isWithin(packageLibPath, source.fullName)) { | |
308 LibraryElement libraryElement = context.getLibraryElement(source); | |
309 if (libraryElement != null) { | |
310 _serializeSingleLibrary(libraryElement); | |
311 } | |
312 } | |
313 } | |
314 // Write the whole package bundle. | |
315 SdkBundleBuilder sdkBundle = new SdkBundleBuilder( | |
Brian Wilkerson
2016/02/23 00:27:01
Probably ought to rename SdkBundleBuilder at some
Paul Berry
2016/02/23 00:59:29
Agreed. I have some other changes I want to make
scheglov
2016/02/23 01:15:28
Yes, it was quite obvious that we need to rename i
| |
316 linkedLibraryUris: linkedLibraryUris, | |
317 linkedLibraries: linkedLibraries, | |
318 unlinkedUnitUris: unlinkedUnitUris, | |
319 unlinkedUnits: unlinkedUnits); | |
320 io.File file = new io.File(options.packageSummaryOutput); | |
321 file.writeAsBytesSync(sdkBundle.toBuffer(), mode: io.FileMode.WRITE_ONLY); | |
322 } | |
323 | |
324 // Process errors. | |
325 _printErrors(); | |
326 return _computeMaxSeverity(); | |
327 } | |
328 | |
329 ErrorSeverity _computeMaxSeverity() { | |
330 ErrorSeverity maxSeverity = ErrorSeverity.NONE; | |
331 for (Source source in explicitSources) { | |
332 AnalysisErrorInfo errorInfo = context.getErrors(source); | |
333 for (AnalysisError error in errorInfo.errors) { | |
334 ProcessedSeverity processedSeverity = | |
335 AnalyzerImpl.processError(error, options, context); | |
336 if (processedSeverity != null) { | |
337 maxSeverity = maxSeverity.max(processedSeverity.severity); | |
338 } | |
339 } | |
340 } | |
341 return maxSeverity; | |
342 } | |
343 | |
344 void _createContext() { | |
345 DirectoryBasedDartSdk sdk = DirectoryBasedDartSdk.defaultSdk; | |
346 sdk.useSummary = true; | |
347 | |
348 // Create the context. | |
349 context = AnalysisEngine.instance.createAnalysisContext(); | |
350 context.typeProvider = sdk.context.typeProvider; | |
351 context.sourceFactory = new SourceFactory(<UriResolver>[ | |
352 new DartUriResolver(sdk), | |
353 new InSummaryPackageUriResolver(options.packageSummaryInputs), | |
354 new PackageMapUriResolver(resourceProvider, <String, List<Folder>>{ | |
355 options.packageName: <Folder>[ | |
356 resourceProvider.getFolder(packageLibPath) | |
357 ], | |
358 }), | |
359 new FileUriResolver() | |
360 ]); | |
361 context.resultProvider = | |
362 new InputPackagesResultProvider(context, options.packageSummaryInputs); | |
363 | |
364 // Set context options. | |
365 Driver.setAnalysisContextOptions( | |
366 context, options, (AnalysisOptionsImpl contextOptions) {}); | |
367 } | |
368 | |
369 /** | |
370 * Create and return a source representing the given [file]. | |
371 */ | |
372 Source _createSourceInContext(File file) { | |
373 Source source = file.createSource(); | |
374 if (context == null) { | |
375 return source; | |
376 } | |
377 Uri uri = context.sourceFactory.restoreUri(source); | |
378 return file.createSource(uri); | |
379 } | |
380 | |
381 /** | |
382 * Print errors for all explicit sources. | |
383 */ | |
384 void _printErrors() { | |
385 StringSink sink = options.machineFormat ? errorSink : outSink; | |
386 ErrorFormatter formatter = new ErrorFormatter( | |
387 sink, | |
388 options, | |
389 (AnalysisError error) => | |
390 AnalyzerImpl.processError(error, options, context)); | |
391 for (Source source in explicitSources) { | |
392 AnalysisErrorInfo errorInfo = context.getErrors(source); | |
393 formatter.formatErrors([errorInfo]); | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * Serialize the library with the given [element]. | |
399 */ | |
400 void _serializeSingleLibrary(LibraryElement element) { | |
401 String uri = element.source.uri.toString(); | |
402 LibrarySerializationResult libraryResult = | |
403 serializeLibrary(element, context.typeProvider, options.strongMode); | |
404 linkedLibraryUris.add(uri); | |
405 linkedLibraries.add(libraryResult.linked); | |
406 unlinkedUnitUris.addAll(libraryResult.unitUris); | |
407 unlinkedUnits.addAll(libraryResult.unlinkedUnits); | |
408 } | |
409 } | |
OLD | NEW |