Index: observatory_pub_packages/analyzer/src/analyzer_impl.dart |
=================================================================== |
--- observatory_pub_packages/analyzer/src/analyzer_impl.dart (revision 0) |
+++ observatory_pub_packages/analyzer/src/analyzer_impl.dart (working copy) |
@@ -0,0 +1,400 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library analyzer_impl; |
+ |
+import 'dart:async'; |
+ |
+import 'dart:io'; |
+ |
+import 'generated/constant.dart'; |
+import 'generated/engine.dart'; |
+import 'generated/element.dart'; |
+import 'generated/error.dart'; |
+import 'generated/java_io.dart'; |
+import 'generated/sdk.dart'; |
+import 'generated/sdk_io.dart'; |
+import 'generated/source_io.dart'; |
+import '../options.dart'; |
+ |
+import 'dart:collection'; |
+ |
+import 'package:analyzer/src/generated/java_core.dart' show JavaSystem; |
+import 'package:analyzer/src/error_formatter.dart'; |
+ |
+/** |
+ * The maximum number of sources for which AST structures should be kept in the cache. |
+ */ |
+const int _MAX_CACHE_SIZE = 512; |
+ |
+DartSdk sdk; |
+ |
+/// Analyzes single library [File]. |
+class AnalyzerImpl { |
+ final String sourcePath; |
+ final CommandLineOptions options; |
+ final int startTime; |
+ |
+ ContentCache contentCache = new ContentCache(); |
+ SourceFactory sourceFactory; |
+ AnalysisContext context; |
+ Source librarySource; |
+ |
+ /// All [Source]s references by the analyzed library. |
+ final Set<Source> sources = new Set<Source>(); |
+ |
+ /// All [AnalysisErrorInfo]s in the analyzed library. |
+ final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>(); |
+ |
+ /// [HashMap] between sources and analysis error infos. |
+ final HashMap<Source, AnalysisErrorInfo> sourceErrorsMap = new HashMap<Source, AnalysisErrorInfo>(); |
+ |
+ AnalyzerImpl(this.sourcePath, this.options, this.startTime) { |
+ if (sdk == null) { |
+ sdk = new DirectoryBasedDartSdk(new JavaFile(options.dartSdkPath)); |
+ } |
+ } |
+ |
+ /** |
+ * Treats the [sourcePath] as the top level library and analyzes it using a |
+ * synchronous algorithm over the analysis engine. If [printMode] is `0`, |
+ * then no error or performance information is printed. If [printMode] is `1`, |
+ * then both will be printed. If [printMode] is `2`, then only performance |
+ * information will be printed, and it will be marked as being for a cold VM. |
+ */ |
+ ErrorSeverity analyzeSync({int printMode : 1}) { |
+ setupForAnalysis(); |
+ return _analyzeSync(printMode); |
+ } |
+ |
+ /** |
+ * Treats the [sourcePath] as the top level library and analyzes it using a |
+ * asynchronous algorithm over the analysis engine. |
+ */ |
+ void analyzeAsync() { |
+ setupForAnalysis(); |
+ _analyzeAsync(); |
+ } |
+ |
+ /** |
+ * Setup local fields such as the analysis context for analysis. |
+ */ |
+ void setupForAnalysis() { |
+ sources.clear(); |
+ errorInfos.clear(); |
+ if (sourcePath == null) { |
+ throw new ArgumentError("sourcePath cannot be null"); |
+ } |
+ JavaFile sourceFile = new JavaFile(sourcePath); |
+ Uri uri = getUri(sourceFile); |
+ librarySource = new FileBasedSource.con2(uri, sourceFile); |
+ |
+ // prepare context |
+ prepareAnalysisContext(sourceFile, librarySource); |
+ } |
+ |
+ /// The sync version of analysis. |
+ ErrorSeverity _analyzeSync(int printMode) { |
+ // don't try to analyze parts |
+ if (context.computeKindOf(librarySource) == SourceKind.PART) { |
+ print("Only libraries can be analyzed."); |
+ print("$sourcePath is a part and can not be analyzed."); |
+ return ErrorSeverity.ERROR; |
+ } |
+ // resolve library |
+ var libraryElement = context.computeLibraryElement(librarySource); |
+ // prepare source and errors |
+ prepareSources(libraryElement); |
+ prepareErrors(); |
+ |
+ // print errors and performance numbers |
+ if (printMode == 1) { |
+ _printErrorsAndPerf(); |
+ } else if (printMode == 2) { |
+ _printColdPerf(); |
+ } |
+ |
+ // compute max severity and set exitCode |
+ ErrorSeverity status = maxErrorSeverity; |
+ if (status == ErrorSeverity.WARNING && options.warningsAreFatal) { |
+ status = ErrorSeverity.ERROR; |
+ } |
+ return status; |
+ } |
+ |
+ /// The async version of the analysis |
+ void _analyzeAsync() { |
+ new Future(context.performAnalysisTask).then((AnalysisResult result) { |
+ List<ChangeNotice> notices = result.changeNotices; |
+ if (result.hasMoreWork) { |
+ // There is more work, record the set of sources, and then call self |
+ // again to perform next task |
+ for (ChangeNotice notice in notices) { |
+ sources.add(notice.source); |
+ sourceErrorsMap[notice.source] = notice; |
+ } |
+ return _analyzeAsync(); |
+ } |
+ // |
+ // There are not any more tasks, set error code and print performance |
+ // numbers. |
+ // |
+ // prepare errors |
+ sourceErrorsMap.forEach((k,v) { |
+ errorInfos.add(sourceErrorsMap[k]); |
+ }); |
+ |
+ // print errors and performance numbers |
+ _printErrorsAndPerf(); |
+ |
+ // compute max severity and set exitCode |
+ ErrorSeverity status = maxErrorSeverity; |
+ if (status == ErrorSeverity.WARNING && options.warningsAreFatal) { |
+ status = ErrorSeverity.ERROR; |
+ } |
+ exitCode = status.ordinal; |
+ }).catchError((ex, st) { |
+ AnalysisEngine.instance.logger.logError("${ex}\n${st}"); |
+ }); |
+ } |
+ |
+ bool _excludeTodo(AnalysisError error) => error.errorCode.type != ErrorType.TODO; |
+ |
+ _printErrorsAndPerf() { |
+ // The following is a hack. We currently print out to stderr to ensure that |
+ // when in batch mode we print to stderr, this is because the prints from |
+ // batch are made to stderr. The reason that options.shouldBatch isn't used |
+ // is because when the argument flags are constructed in BatchRunner and |
+ // passed in from batch mode which removes the batch flag to prevent the |
+ // "cannot have the batch flag and source file" error message. |
+ IOSink sink = options.machineFormat ? stderr : stdout; |
+ |
+ // print errors |
+ ErrorFormatter formatter = new ErrorFormatter(sink, options, _excludeTodo); |
+ formatter.formatErrors(errorInfos); |
+ |
+ // print performance numbers |
+ if (options.perf || options.warmPerf) { |
+ int totalTime = JavaSystem.currentTimeMillis() - startTime; |
+ int ioTime = PerformanceStatistics.io.result; |
+ int scanTime = PerformanceStatistics.scan.result; |
+ int parseTime = PerformanceStatistics.parse.result; |
+ int resolveTime = PerformanceStatistics.resolve.result; |
+ int errorsTime = PerformanceStatistics.errors.result; |
+ int hintsTime = PerformanceStatistics.hints.result; |
+ int angularTime = PerformanceStatistics.angular.result; |
+ stdout.writeln("io:$ioTime"); |
+ stdout.writeln("scan:$scanTime"); |
+ stdout.writeln("parse:$parseTime"); |
+ stdout.writeln("resolve:$resolveTime"); |
+ stdout.writeln("errors:$errorsTime"); |
+ stdout.writeln("hints:$hintsTime"); |
+ stdout.writeln("angular:$angularTime"); |
+ stdout.writeln("other:${totalTime |
+ - (ioTime + scanTime + parseTime + resolveTime + errorsTime + hintsTime |
+ + angularTime)}"); |
+ stdout.writeln("total:$totalTime"); |
+ } |
+ } |
+ |
+ _printColdPerf() { |
+ // print cold VM performance numbers |
+ int totalTime = JavaSystem.currentTimeMillis() - startTime; |
+ int ioTime = PerformanceStatistics.io.result; |
+ int scanTime = PerformanceStatistics.scan.result; |
+ int parseTime = PerformanceStatistics.parse.result; |
+ int resolveTime = PerformanceStatistics.resolve.result; |
+ int errorsTime = PerformanceStatistics.errors.result; |
+ int hintsTime = PerformanceStatistics.hints.result; |
+ int angularTime = PerformanceStatistics.angular.result; |
+ stdout.writeln("io-cold:$ioTime"); |
+ stdout.writeln("scan-cold:$scanTime"); |
+ stdout.writeln("parse-cold:$parseTime"); |
+ stdout.writeln("resolve-cold:$resolveTime"); |
+ stdout.writeln("errors-cold:$errorsTime"); |
+ stdout.writeln("hints-cold:$hintsTime"); |
+ stdout.writeln("angular-cold:$angularTime"); |
+ stdout.writeln("other-cold:${totalTime |
+ - (ioTime + scanTime + parseTime + resolveTime + errorsTime + hintsTime |
+ + angularTime)}"); |
+ stdout.writeln("total-cold:$totalTime"); |
+ } |
+ |
+ /// Returns the maximal [ErrorSeverity] of the recorded errors. |
+ ErrorSeverity get maxErrorSeverity { |
+ var status = ErrorSeverity.NONE; |
+ for (AnalysisErrorInfo errorInfo in errorInfos) { |
+ for (AnalysisError error in errorInfo.errors) { |
+ var severity = error.errorCode.errorSeverity; |
+ status = status.max(severity); |
+ } |
+ } |
+ return status; |
+ } |
+ |
+ void prepareAnalysisContext(JavaFile sourceFile, Source source) { |
+ List<UriResolver> resolvers = [new DartUriResolver(sdk), new FileUriResolver()]; |
+ // may be add package resolver |
+ { |
+ JavaFile packageDirectory; |
+ if (options.packageRootPath != null) { |
+ packageDirectory = new JavaFile(options.packageRootPath); |
+ } else { |
+ packageDirectory = getPackageDirectoryFor(sourceFile); |
+ } |
+ if (packageDirectory != null) { |
+ resolvers.add(new PackageUriResolver([packageDirectory])); |
+ } |
+ } |
+ sourceFactory = new SourceFactory(resolvers); |
+ context = AnalysisEngine.instance.createAnalysisContext(); |
+ context.sourceFactory = sourceFactory; |
+ Map<String, String> definedVariables = options.definedVariables; |
+ if (!definedVariables.isEmpty) { |
+ DeclaredVariables declaredVariables = context.declaredVariables; |
+ definedVariables.forEach((String variableName, String value) { |
+ declaredVariables.define(variableName, value); |
+ }); |
+ } |
+ // Uncomment the following to have errors reported on stdout and stderr |
+ AnalysisEngine.instance.logger = new StdLogger(options.log); |
+ |
+ // set options for context |
+ AnalysisOptionsImpl contextOptions = new AnalysisOptionsImpl(); |
+ contextOptions.cacheSize = _MAX_CACHE_SIZE; |
+ contextOptions.hint = !options.disableHints; |
+ contextOptions.enableAsync = options.enableAsync; |
+ contextOptions.enableEnum = options.enableEnum; |
+ context.analysisOptions = contextOptions; |
+ |
+ // Create and add a ChangeSet |
+ ChangeSet changeSet = new ChangeSet(); |
+ changeSet.addedSource(source); |
+ context.applyChanges(changeSet); |
+ } |
+ |
+ void addCompilationUnitSource(CompilationUnitElement unit, Set<LibraryElement> libraries, |
+ Set<CompilationUnitElement> units) { |
+ if (unit == null || units.contains(unit)) { |
+ return; |
+ } |
+ units.add(unit); |
+ sources.add(unit.source); |
+ } |
+ |
+ void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries, |
+ Set<CompilationUnitElement> units) { |
+ if (library == null || !libraries.add(library) ) { |
+ return; |
+ } |
+ // may be skip library |
+ { |
+ UriKind uriKind = library.source.uriKind; |
+ // Optionally skip package: libraries. |
+ if (!options.showPackageWarnings && uriKind == UriKind.PACKAGE_URI) { |
+ return; |
+ } |
+ // Optionally skip SDK libraries. |
+ if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) { |
+ return; |
+ } |
+ } |
+ // add compilation units |
+ addCompilationUnitSource(library.definingCompilationUnit, libraries, units); |
+ for (CompilationUnitElement child in library.parts) { |
+ addCompilationUnitSource(child, libraries, units); |
+ } |
+ // add referenced libraries |
+ for (LibraryElement child in library.importedLibraries) { |
+ addLibrarySources(child, libraries, units); |
+ } |
+ for (LibraryElement child in library.exportedLibraries) { |
+ addLibrarySources(child, libraries, units); |
+ } |
+ } |
+ |
+ /// Fills [sources]. |
+ void prepareSources(LibraryElement library) { |
+ var units = new Set<CompilationUnitElement>(); |
+ var libraries = new Set<LibraryElement>(); |
+ addLibrarySources(library, libraries, units); |
+ } |
+ |
+ /// Fills [errorInfos] using [sources]. |
+ void prepareErrors() { |
+ for (Source source in sources) { |
+ context.computeErrors(source); |
+ var sourceErrors = context.getErrors(source); |
+ errorInfos.add(sourceErrors); |
+ } |
+ } |
+ |
+ static JavaFile getPackageDirectoryFor(JavaFile sourceFile) { |
+ // we are going to ask parent file, so get absolute path |
+ sourceFile = sourceFile.getAbsoluteFile(); |
+ // look in the containing directories |
+ JavaFile dir = sourceFile.getParentFile(); |
+ while (dir != null) { |
+ JavaFile packagesDir = new JavaFile.relative(dir, "packages"); |
+ if (packagesDir.exists()) { |
+ return packagesDir; |
+ } |
+ dir = dir.getParentFile(); |
+ } |
+ // not found |
+ return null; |
+ } |
+ |
+ /** |
+ * Returns the [Uri] for the given input file. |
+ * |
+ * Usually it is a `file:` [Uri], but if [file] is located in the `lib` |
+ * directory of the [sdk], then returns a `dart:` [Uri]. |
+ */ |
+ static Uri getUri(JavaFile file) { |
+ // may be file in SDK |
+ { |
+ Source source = sdk.fromFileUri(file.toURI()); |
+ if (source != null) { |
+ return source.uri; |
+ } |
+ } |
+ // some generic file |
+ return file.toURI(); |
+ } |
+} |
+ |
+/** |
+ * This [Logger] prints out information comments to [stdout] and error messages |
+ * to [stderr]. |
+ */ |
+class StdLogger extends Logger { |
+ final bool log; |
+ |
+ StdLogger(this.log); |
+ |
+ @override |
+ void logError(String message) { |
+ stderr.writeln(message); |
+ } |
+ |
+ @override |
+ void logError2(String message, Exception exception) { |
+ stderr.writeln(message); |
+ } |
+ |
+ @override |
+ void logInformation(String message) { |
+ if (log) { |
+ stdout.writeln(message); |
+ } |
+ } |
+ |
+ @override |
+ void logInformation2(String message, Exception exception) { |
+ if (log) { |
+ stdout.writeln(message); |
+ } |
+ } |
+} |