OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, 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_impl; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'dart:io'; |
| 10 |
| 11 import 'generated/constant.dart'; |
| 12 import 'generated/engine.dart'; |
| 13 import 'generated/element.dart'; |
| 14 import 'generated/error.dart'; |
| 15 import 'generated/java_io.dart'; |
| 16 import 'generated/sdk.dart'; |
| 17 import 'generated/sdk_io.dart'; |
| 18 import 'generated/source_io.dart'; |
| 19 import '../options.dart'; |
| 20 |
| 21 import 'dart:collection'; |
| 22 |
| 23 import 'package:analyzer/src/generated/java_core.dart' show JavaSystem; |
| 24 import 'package:analyzer/src/error_formatter.dart'; |
| 25 |
| 26 /** |
| 27 * The maximum number of sources for which AST structures should be kept in the
cache. |
| 28 */ |
| 29 const int _MAX_CACHE_SIZE = 512; |
| 30 |
| 31 DartSdk sdk; |
| 32 |
| 33 /// Analyzes single library [File]. |
| 34 class AnalyzerImpl { |
| 35 final String sourcePath; |
| 36 final CommandLineOptions options; |
| 37 final int startTime; |
| 38 |
| 39 ContentCache contentCache = new ContentCache(); |
| 40 SourceFactory sourceFactory; |
| 41 AnalysisContext context; |
| 42 Source librarySource; |
| 43 |
| 44 /// All [Source]s references by the analyzed library. |
| 45 final Set<Source> sources = new Set<Source>(); |
| 46 |
| 47 /// All [AnalysisErrorInfo]s in the analyzed library. |
| 48 final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>(); |
| 49 |
| 50 /// [HashMap] between sources and analysis error infos. |
| 51 final HashMap<Source, AnalysisErrorInfo> sourceErrorsMap = new HashMap<Source,
AnalysisErrorInfo>(); |
| 52 |
| 53 AnalyzerImpl(this.sourcePath, this.options, this.startTime) { |
| 54 if (sdk == null) { |
| 55 sdk = new DirectoryBasedDartSdk(new JavaFile(options.dartSdkPath)); |
| 56 } |
| 57 } |
| 58 |
| 59 /** |
| 60 * Treats the [sourcePath] as the top level library and analyzes it using a |
| 61 * synchronous algorithm over the analysis engine. If [printMode] is `0`, |
| 62 * then no error or performance information is printed. If [printMode] is `1`, |
| 63 * then both will be printed. If [printMode] is `2`, then only performance |
| 64 * information will be printed, and it will be marked as being for a cold VM. |
| 65 */ |
| 66 ErrorSeverity analyzeSync({int printMode : 1}) { |
| 67 setupForAnalysis(); |
| 68 return _analyzeSync(printMode); |
| 69 } |
| 70 |
| 71 /** |
| 72 * Treats the [sourcePath] as the top level library and analyzes it using a |
| 73 * asynchronous algorithm over the analysis engine. |
| 74 */ |
| 75 void analyzeAsync() { |
| 76 setupForAnalysis(); |
| 77 _analyzeAsync(); |
| 78 } |
| 79 |
| 80 /** |
| 81 * Setup local fields such as the analysis context for analysis. |
| 82 */ |
| 83 void setupForAnalysis() { |
| 84 sources.clear(); |
| 85 errorInfos.clear(); |
| 86 if (sourcePath == null) { |
| 87 throw new ArgumentError("sourcePath cannot be null"); |
| 88 } |
| 89 JavaFile sourceFile = new JavaFile(sourcePath); |
| 90 Uri uri = getUri(sourceFile); |
| 91 librarySource = new FileBasedSource.con2(uri, sourceFile); |
| 92 |
| 93 // prepare context |
| 94 prepareAnalysisContext(sourceFile, librarySource); |
| 95 } |
| 96 |
| 97 /// The sync version of analysis. |
| 98 ErrorSeverity _analyzeSync(int printMode) { |
| 99 // don't try to analyze parts |
| 100 if (context.computeKindOf(librarySource) == SourceKind.PART) { |
| 101 print("Only libraries can be analyzed."); |
| 102 print("$sourcePath is a part and can not be analyzed."); |
| 103 return ErrorSeverity.ERROR; |
| 104 } |
| 105 // resolve library |
| 106 var libraryElement = context.computeLibraryElement(librarySource); |
| 107 // prepare source and errors |
| 108 prepareSources(libraryElement); |
| 109 prepareErrors(); |
| 110 |
| 111 // print errors and performance numbers |
| 112 if (printMode == 1) { |
| 113 _printErrorsAndPerf(); |
| 114 } else if (printMode == 2) { |
| 115 _printColdPerf(); |
| 116 } |
| 117 |
| 118 // compute max severity and set exitCode |
| 119 ErrorSeverity status = maxErrorSeverity; |
| 120 if (status == ErrorSeverity.WARNING && options.warningsAreFatal) { |
| 121 status = ErrorSeverity.ERROR; |
| 122 } |
| 123 return status; |
| 124 } |
| 125 |
| 126 /// The async version of the analysis |
| 127 void _analyzeAsync() { |
| 128 new Future(context.performAnalysisTask).then((AnalysisResult result) { |
| 129 List<ChangeNotice> notices = result.changeNotices; |
| 130 if (result.hasMoreWork) { |
| 131 // There is more work, record the set of sources, and then call self |
| 132 // again to perform next task |
| 133 for (ChangeNotice notice in notices) { |
| 134 sources.add(notice.source); |
| 135 sourceErrorsMap[notice.source] = notice; |
| 136 } |
| 137 return _analyzeAsync(); |
| 138 } |
| 139 // |
| 140 // There are not any more tasks, set error code and print performance |
| 141 // numbers. |
| 142 // |
| 143 // prepare errors |
| 144 sourceErrorsMap.forEach((k,v) { |
| 145 errorInfos.add(sourceErrorsMap[k]); |
| 146 }); |
| 147 |
| 148 // print errors and performance numbers |
| 149 _printErrorsAndPerf(); |
| 150 |
| 151 // compute max severity and set exitCode |
| 152 ErrorSeverity status = maxErrorSeverity; |
| 153 if (status == ErrorSeverity.WARNING && options.warningsAreFatal) { |
| 154 status = ErrorSeverity.ERROR; |
| 155 } |
| 156 exitCode = status.ordinal; |
| 157 }).catchError((ex, st) { |
| 158 AnalysisEngine.instance.logger.logError("${ex}\n${st}"); |
| 159 }); |
| 160 } |
| 161 |
| 162 bool _excludeTodo(AnalysisError error) => error.errorCode.type != ErrorType.TO
DO; |
| 163 |
| 164 _printErrorsAndPerf() { |
| 165 // The following is a hack. We currently print out to stderr to ensure that |
| 166 // when in batch mode we print to stderr, this is because the prints from |
| 167 // batch are made to stderr. The reason that options.shouldBatch isn't used |
| 168 // is because when the argument flags are constructed in BatchRunner and |
| 169 // passed in from batch mode which removes the batch flag to prevent the |
| 170 // "cannot have the batch flag and source file" error message. |
| 171 IOSink sink = options.machineFormat ? stderr : stdout; |
| 172 |
| 173 // print errors |
| 174 ErrorFormatter formatter = new ErrorFormatter(sink, options, _excludeTodo); |
| 175 formatter.formatErrors(errorInfos); |
| 176 |
| 177 // print performance numbers |
| 178 if (options.perf || options.warmPerf) { |
| 179 int totalTime = JavaSystem.currentTimeMillis() - startTime; |
| 180 int ioTime = PerformanceStatistics.io.result; |
| 181 int scanTime = PerformanceStatistics.scan.result; |
| 182 int parseTime = PerformanceStatistics.parse.result; |
| 183 int resolveTime = PerformanceStatistics.resolve.result; |
| 184 int errorsTime = PerformanceStatistics.errors.result; |
| 185 int hintsTime = PerformanceStatistics.hints.result; |
| 186 int angularTime = PerformanceStatistics.angular.result; |
| 187 stdout.writeln("io:$ioTime"); |
| 188 stdout.writeln("scan:$scanTime"); |
| 189 stdout.writeln("parse:$parseTime"); |
| 190 stdout.writeln("resolve:$resolveTime"); |
| 191 stdout.writeln("errors:$errorsTime"); |
| 192 stdout.writeln("hints:$hintsTime"); |
| 193 stdout.writeln("angular:$angularTime"); |
| 194 stdout.writeln("other:${totalTime |
| 195 - (ioTime + scanTime + parseTime + resolveTime + errorsTime + hintsTim
e |
| 196 + angularTime)}"); |
| 197 stdout.writeln("total:$totalTime"); |
| 198 } |
| 199 } |
| 200 |
| 201 _printColdPerf() { |
| 202 // print cold VM performance numbers |
| 203 int totalTime = JavaSystem.currentTimeMillis() - startTime; |
| 204 int ioTime = PerformanceStatistics.io.result; |
| 205 int scanTime = PerformanceStatistics.scan.result; |
| 206 int parseTime = PerformanceStatistics.parse.result; |
| 207 int resolveTime = PerformanceStatistics.resolve.result; |
| 208 int errorsTime = PerformanceStatistics.errors.result; |
| 209 int hintsTime = PerformanceStatistics.hints.result; |
| 210 int angularTime = PerformanceStatistics.angular.result; |
| 211 stdout.writeln("io-cold:$ioTime"); |
| 212 stdout.writeln("scan-cold:$scanTime"); |
| 213 stdout.writeln("parse-cold:$parseTime"); |
| 214 stdout.writeln("resolve-cold:$resolveTime"); |
| 215 stdout.writeln("errors-cold:$errorsTime"); |
| 216 stdout.writeln("hints-cold:$hintsTime"); |
| 217 stdout.writeln("angular-cold:$angularTime"); |
| 218 stdout.writeln("other-cold:${totalTime |
| 219 - (ioTime + scanTime + parseTime + resolveTime + errorsTime + hintsTime |
| 220 + angularTime)}"); |
| 221 stdout.writeln("total-cold:$totalTime"); |
| 222 } |
| 223 |
| 224 /// Returns the maximal [ErrorSeverity] of the recorded errors. |
| 225 ErrorSeverity get maxErrorSeverity { |
| 226 var status = ErrorSeverity.NONE; |
| 227 for (AnalysisErrorInfo errorInfo in errorInfos) { |
| 228 for (AnalysisError error in errorInfo.errors) { |
| 229 var severity = error.errorCode.errorSeverity; |
| 230 status = status.max(severity); |
| 231 } |
| 232 } |
| 233 return status; |
| 234 } |
| 235 |
| 236 void prepareAnalysisContext(JavaFile sourceFile, Source source) { |
| 237 List<UriResolver> resolvers = [new DartUriResolver(sdk), new FileUriResolver
()]; |
| 238 // may be add package resolver |
| 239 { |
| 240 JavaFile packageDirectory; |
| 241 if (options.packageRootPath != null) { |
| 242 packageDirectory = new JavaFile(options.packageRootPath); |
| 243 } else { |
| 244 packageDirectory = getPackageDirectoryFor(sourceFile); |
| 245 } |
| 246 if (packageDirectory != null) { |
| 247 resolvers.add(new PackageUriResolver([packageDirectory])); |
| 248 } |
| 249 } |
| 250 sourceFactory = new SourceFactory(resolvers); |
| 251 context = AnalysisEngine.instance.createAnalysisContext(); |
| 252 context.sourceFactory = sourceFactory; |
| 253 Map<String, String> definedVariables = options.definedVariables; |
| 254 if (!definedVariables.isEmpty) { |
| 255 DeclaredVariables declaredVariables = context.declaredVariables; |
| 256 definedVariables.forEach((String variableName, String value) { |
| 257 declaredVariables.define(variableName, value); |
| 258 }); |
| 259 } |
| 260 // Uncomment the following to have errors reported on stdout and stderr |
| 261 AnalysisEngine.instance.logger = new StdLogger(options.log); |
| 262 |
| 263 // set options for context |
| 264 AnalysisOptionsImpl contextOptions = new AnalysisOptionsImpl(); |
| 265 contextOptions.cacheSize = _MAX_CACHE_SIZE; |
| 266 contextOptions.hint = !options.disableHints; |
| 267 contextOptions.enableAsync = options.enableAsync; |
| 268 contextOptions.enableEnum = options.enableEnum; |
| 269 context.analysisOptions = contextOptions; |
| 270 |
| 271 // Create and add a ChangeSet |
| 272 ChangeSet changeSet = new ChangeSet(); |
| 273 changeSet.addedSource(source); |
| 274 context.applyChanges(changeSet); |
| 275 } |
| 276 |
| 277 void addCompilationUnitSource(CompilationUnitElement unit, Set<LibraryElement>
libraries, |
| 278 Set<CompilationUnitElement> units) { |
| 279 if (unit == null || units.contains(unit)) { |
| 280 return; |
| 281 } |
| 282 units.add(unit); |
| 283 sources.add(unit.source); |
| 284 } |
| 285 |
| 286 void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries, |
| 287 Set<CompilationUnitElement> units) { |
| 288 if (library == null || !libraries.add(library) ) { |
| 289 return; |
| 290 } |
| 291 // may be skip library |
| 292 { |
| 293 UriKind uriKind = library.source.uriKind; |
| 294 // Optionally skip package: libraries. |
| 295 if (!options.showPackageWarnings && uriKind == UriKind.PACKAGE_URI) { |
| 296 return; |
| 297 } |
| 298 // Optionally skip SDK libraries. |
| 299 if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) { |
| 300 return; |
| 301 } |
| 302 } |
| 303 // add compilation units |
| 304 addCompilationUnitSource(library.definingCompilationUnit, libraries, units); |
| 305 for (CompilationUnitElement child in library.parts) { |
| 306 addCompilationUnitSource(child, libraries, units); |
| 307 } |
| 308 // add referenced libraries |
| 309 for (LibraryElement child in library.importedLibraries) { |
| 310 addLibrarySources(child, libraries, units); |
| 311 } |
| 312 for (LibraryElement child in library.exportedLibraries) { |
| 313 addLibrarySources(child, libraries, units); |
| 314 } |
| 315 } |
| 316 |
| 317 /// Fills [sources]. |
| 318 void prepareSources(LibraryElement library) { |
| 319 var units = new Set<CompilationUnitElement>(); |
| 320 var libraries = new Set<LibraryElement>(); |
| 321 addLibrarySources(library, libraries, units); |
| 322 } |
| 323 |
| 324 /// Fills [errorInfos] using [sources]. |
| 325 void prepareErrors() { |
| 326 for (Source source in sources) { |
| 327 context.computeErrors(source); |
| 328 var sourceErrors = context.getErrors(source); |
| 329 errorInfos.add(sourceErrors); |
| 330 } |
| 331 } |
| 332 |
| 333 static JavaFile getPackageDirectoryFor(JavaFile sourceFile) { |
| 334 // we are going to ask parent file, so get absolute path |
| 335 sourceFile = sourceFile.getAbsoluteFile(); |
| 336 // look in the containing directories |
| 337 JavaFile dir = sourceFile.getParentFile(); |
| 338 while (dir != null) { |
| 339 JavaFile packagesDir = new JavaFile.relative(dir, "packages"); |
| 340 if (packagesDir.exists()) { |
| 341 return packagesDir; |
| 342 } |
| 343 dir = dir.getParentFile(); |
| 344 } |
| 345 // not found |
| 346 return null; |
| 347 } |
| 348 |
| 349 /** |
| 350 * Returns the [Uri] for the given input file. |
| 351 * |
| 352 * Usually it is a `file:` [Uri], but if [file] is located in the `lib` |
| 353 * directory of the [sdk], then returns a `dart:` [Uri]. |
| 354 */ |
| 355 static Uri getUri(JavaFile file) { |
| 356 // may be file in SDK |
| 357 { |
| 358 Source source = sdk.fromFileUri(file.toURI()); |
| 359 if (source != null) { |
| 360 return source.uri; |
| 361 } |
| 362 } |
| 363 // some generic file |
| 364 return file.toURI(); |
| 365 } |
| 366 } |
| 367 |
| 368 /** |
| 369 * This [Logger] prints out information comments to [stdout] and error messages |
| 370 * to [stderr]. |
| 371 */ |
| 372 class StdLogger extends Logger { |
| 373 final bool log; |
| 374 |
| 375 StdLogger(this.log); |
| 376 |
| 377 @override |
| 378 void logError(String message) { |
| 379 stderr.writeln(message); |
| 380 } |
| 381 |
| 382 @override |
| 383 void logError2(String message, Exception exception) { |
| 384 stderr.writeln(message); |
| 385 } |
| 386 |
| 387 @override |
| 388 void logInformation(String message) { |
| 389 if (log) { |
| 390 stdout.writeln(message); |
| 391 } |
| 392 } |
| 393 |
| 394 @override |
| 395 void logInformation2(String message, Exception exception) { |
| 396 if (log) { |
| 397 stdout.writeln(message); |
| 398 } |
| 399 } |
| 400 } |
OLD | NEW |