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

Unified Diff: packages/analyzer/tool/task_dependency_graph/generate.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years 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 side-by-side diff with in-line comments
Download patch
Index: packages/analyzer/tool/task_dependency_graph/generate.dart
diff --git a/packages/analyzer/tool/task_dependency_graph/generate.dart b/packages/analyzer/tool/task_dependency_graph/generate.dart
new file mode 100644
index 0000000000000000000000000000000000000000..235c2b9d032b7bee4592cab847b620d9135478b9
--- /dev/null
+++ b/packages/analyzer/tool/task_dependency_graph/generate.dart
@@ -0,0 +1,346 @@
+// Copyright (c) 2015, 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.
+
+/**
+ * This file contains code to output a description of tasks and their
+ * dependencies in ".dot" format. Prior to running, the user should run "pub
+ * get" in the analyzer directory to ensure that a "packages" folder exists.
+ *
+ * TODO(paulberry):
+ * - Add general.dart and html.dart for completeness.
+ * - Use Graphviz's "record" feature to produce more compact output
+ * (http://www.graphviz.org/content/node-shapes#record)
+ * - Produce a warning if a result descriptor is found which isn't the output
+ * of exactly one task.
+ * - Convert this tool to use package_config to find the package map.
+ */
+library task_dependency_graph.generate;
+
+import 'dart:io' hide File;
+import 'dart:io' as io;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_io.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/sdk_io.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/source_io.dart';
+import 'package:path/path.dart' as path;
+
+/**
+ * Generate the target .dot file.
+ */
+main() {
+ new Driver().generateFile();
+}
+
+typedef void GetterFinderCallback(PropertyAccessorElement element);
+
+class Driver {
+ PhysicalResourceProvider resourceProvider;
+ AnalysisContext context;
+ InterfaceType resultDescriptorType;
+ InterfaceType listOfResultDescriptorType;
+ ClassElement enginePluginClass;
+ CompilationUnitElement taskUnitElement;
+ InterfaceType extensionPointIdType;
+ final String rootDir;
+
+ Driver()
+ : rootDir =
+ findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
+
+ /**
+ * Get an [io.File] object corresponding to the file in which the generated
+ * graph should be output.
+ */
+ io.File get file => new io.File(
+ path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot'));
+
+ /**
+ * Determine if the output [file] contains the expected contents.
+ */
+ bool checkFile() {
+ String expectedContents = generateFileContents();
+ String actualContents = file.readAsStringSync();
+ // Normalize Windows line endings to Unix line endings so that the
+ // comparison doesn't fail on Windows.
+ actualContents = actualContents.replaceAll('\r\n', '\n');
+ return expectedContents == actualContents;
+ }
+
+ /**
+ * Starting at [node], find all calls to registerExtension() which refer to
+ * the given [extensionIdVariable], and execute [callback] for the associated
+ * result descriptors.
+ */
+ void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable,
+ void callback(descriptorName)) {
+ Set<PropertyAccessorElement> resultDescriptors =
+ new Set<PropertyAccessorElement>();
+ node.accept(new ExtensionFinder(
+ resultDescriptorType, extensionIdVariable, resultDescriptors.add));
+ for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
+ callback(resultDescriptor.name);
+ }
+ }
+
+ /**
+ * Starting at [node], find all references to a getter of type
+ * `List<ResultDescriptor>`, and execute [callback] on the getter names.
+ */
+ void findResultDescriptorLists(
+ AstNode node, void callback(String descriptorListName)) {
+ Set<PropertyAccessorElement> resultDescriptorLists =
+ new Set<PropertyAccessorElement>();
+ node.accept(new GetterFinder(
+ listOfResultDescriptorType, resultDescriptorLists.add));
+ for (PropertyAccessorElement resultDescriptorList
+ in resultDescriptorLists) {
+ // We only care about result descriptor lists associated with getters in
+ // the engine plugin class.
+ if (resultDescriptorList.enclosingElement != enginePluginClass) {
+ continue;
+ }
+ callback(resultDescriptorList.name);
+ }
+ }
+
+ void findResultDescriptors(
+ AstNode node, void callback(String descriptorName)) {
+ Set<PropertyAccessorElement> resultDescriptors =
+ new Set<PropertyAccessorElement>();
+ node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add));
+ for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
+ callback(resultDescriptor.name);
+ }
+ }
+
+ /**
+ * Generate the task dependency graph and write it to the output [file].
+ */
+ void generateFile() {
+ String fileContents = generateFileContents();
+ file.writeAsStringSync(fileContents);
+ }
+
+ /**
+ * Generate the task dependency graph and return it as a [String].
+ */
+ String generateFileContents() {
+ List<String> lines = <String>[];
+ resourceProvider = PhysicalResourceProvider.INSTANCE;
+ DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
+ context = AnalysisEngine.instance.createAnalysisContext();
+ String packageRootPath;
+ if (Platform.packageRoot.isNotEmpty) {
+ packageRootPath = Platform.packageRoot;
+ } else {
+ packageRootPath = path.join(rootDir, 'packages');
+ }
+ JavaFile packagesDir = new JavaFile(packageRootPath);
+ List<UriResolver> uriResolvers = [
+ new DartUriResolver(sdk),
+ new PackageUriResolver(<JavaFile>[packagesDir]),
+ new FileUriResolver()
+ ];
+ context.sourceFactory = new SourceFactory(uriResolvers);
+ Source dartDartSource =
+ setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
+ Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart'));
+ Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
+ Source enginePluginSource =
+ setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart'));
+ CompilationUnitElement modelElement = getUnit(modelSource).element;
+ InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
+ DartType dynamicType = context.typeProvider.dynamicType;
+ resultDescriptorType = modelElement
+ .getType('ResultDescriptor')
+ .type
+ .substitute4([dynamicType]);
+ listOfResultDescriptorType =
+ context.typeProvider.listType.substitute4([resultDescriptorType]);
+ CompilationUnitElement enginePluginUnitElement =
+ getUnit(enginePluginSource).element;
+ enginePluginClass = enginePluginUnitElement.getType('EnginePlugin');
+ extensionPointIdType =
+ enginePluginUnitElement.getType('ExtensionPointId').type;
+ CompilationUnit dartDartUnit = getUnit(dartDartSource);
+ CompilationUnitElement dartDartUnitElement = dartDartUnit.element;
+ CompilationUnit taskUnit = getUnit(taskSource);
+ taskUnitElement = taskUnit.element;
+ Set<String> results = new Set<String>();
+ Set<String> resultLists = new Set<String>();
+ for (ClassElement cls in dartDartUnitElement.types) {
+ if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
+ String task = cls.name;
+ AstNode buildInputsAst = cls.getMethod('buildInputs').computeNode();
+ findResultDescriptors(buildInputsAst, (String input) {
+ results.add(input);
+ lines.add(' $input -> $task');
+ });
+ findResultDescriptorLists(buildInputsAst, (String input) {
+ resultLists.add(input);
+ lines.add(' $input -> $task');
+ });
+ findResultDescriptors(cls.getField('DESCRIPTOR').computeNode(), (String out) {
+ results.add(out);
+ lines.add(' $task -> $out');
+ });
+ }
+ }
+ AstNode enginePluginAst = enginePluginUnitElement.computeNode();
+ for (String resultList in resultLists) {
+ lines.add(' $resultList [shape=hexagon]');
+ TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList);
+ findExtensions(enginePluginAst, extensionIdVariable, (String extension) {
+ results.add(extension);
+ lines.add(' $extension -> $resultList');
+ });
+ }
+ for (String result in results) {
+ lines.add(' $result [shape=box]');
+ }
+ lines.sort();
+ return '''
+// Copyright (c) 2015, 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.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use the script
+// "pkg/analyzer/tool/task_dependency_graph/generate.dart".
+//
+// To render this graph using Graphviz (www.graphviz.org) use the command:
+// "dot tasks.dot -Tpdf -O".
+digraph G {
+${lines.join('\n')}
+}
+''';
+ }
+
+ CompilationUnit getUnit(Source source) =>
+ context.resolveCompilationUnit2(source, source);
+
+ Source setupSource(String filename) {
+ String filePath = path.join(rootDir, filename);
+ File file = resourceProvider.getResource(filePath);
+ Source source = file.createSource();
+ Uri restoredUri = context.sourceFactory.restoreUri(source);
+ if (restoredUri != null) {
+ source = file.createSource(restoredUri);
+ }
+ ChangeSet changeSet = new ChangeSet();
+ changeSet.addedSource(source);
+ context.applyChanges(changeSet);
+ return source;
+ }
+
+ /**
+ * Find the result list getter having name [resultListGetterName] in the
+ * [EnginePlugin] class, and use the [ExtensionPointId] annotation on that
+ * getter to find the associated [TopLevelVariableElement] which can be used
+ * to register extensions for that getter.
+ */
+ TopLevelVariableElement _getExtensionId(String resultListGetterName) {
+ PropertyAccessorElement getter =
+ enginePluginClass.getGetter(resultListGetterName);
+ for (ElementAnnotation annotation in getter.metadata) {
+ DartObjectImpl annotationValue = annotation.constantValue;
+ if (annotationValue.type.isSubtypeOf(extensionPointIdType)) {
+ String extensionPointId =
+ annotationValue.fields['extensionPointId'].toStringValue();
+ for (TopLevelVariableElement variable
+ in taskUnitElement.topLevelVariables) {
+ if (variable.name == extensionPointId) {
+ return variable;
+ }
+ }
+ }
+ }
+ throw new Exception(
+ 'Could not find extension ID corresponding to $resultListGetterName');
+ }
+
+ /**
+ * Find the root directory of the analyzer package by proceeding
+ * upward to the 'tool' dir, and then going up one more directory.
+ */
+ static String findRoot(String pathname) {
+ while (path.basename(pathname) != 'tool') {
+ String parent = path.dirname(pathname);
+ if (parent.length >= pathname.length) {
+ throw new Exception("Can't find root directory");
+ }
+ pathname = parent;
+ }
+ return path.dirname(pathname);
+ }
+}
+
+/**
+ * Visitor that finds calls that register extension points. Specifically, we
+ * look for calls of the form `method(extensionIdVariable, resultDescriptor)`,
+ * where `resultDescriptor` has type [resultDescriptorType], and we pass the
+ * corresponding result descriptor names to [callback].
+ */
+class ExtensionFinder extends GeneralizingAstVisitor {
+ final InterfaceType resultDescriptorType;
+ final TopLevelVariableElement extensionIdVariable;
+ final GetterFinderCallback callback;
+
+ ExtensionFinder(
+ this.resultDescriptorType, this.extensionIdVariable, this.callback);
+
+ @override
+ visitIdentifier(Identifier node) {
+ Element element = node.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.variable == extensionIdVariable) {
+ AstNode parent = node.parent;
+ if (parent is ArgumentList &&
+ parent.arguments.length == 2 &&
+ parent.arguments[0] == node) {
+ Expression extension = parent.arguments[1];
+ if (extension is Identifier) {
+ Element element = extension.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.returnType.isSubtypeOf(resultDescriptorType)) {
+ callback(element);
+ return;
+ }
+ }
+ }
+ throw new Exception('Could not decode extension setup: $parent');
+ }
+ }
+}
+
+/**
+ * Visitor that finds references to getters having a specific type (or a
+ * subtype of that type)
+ */
+class GetterFinder extends GeneralizingAstVisitor {
+ final InterfaceType type;
+ final GetterFinderCallback callback;
+
+ GetterFinder(this.type, this.callback);
+
+ @override
+ visitIdentifier(Identifier node) {
+ Element element = node.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.returnType.isSubtypeOf(type)) {
+ callback(element);
+ }
+ }
+}
« no previous file with comments | « packages/analyzer/tool/task_dependency_graph/check_test.dart ('k') | packages/analyzer/tool/task_dependency_graph/tasks.dot » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698