| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 /** | 5 /** |
| 6 * This file contains code to output a description of tasks and their | 6 * This file contains code to output a description of tasks and their |
| 7 * dependencies in ".dot" format. Prior to running, the user should run "pub | 7 * dependencies in ".dot" format. Prior to running, the user should run "pub |
| 8 * get" in the analyzer directory to ensure that a "packages" folder exists. | 8 * get" in the analyzer directory to ensure that a "packages" folder exists. |
| 9 * | 9 * |
| 10 * The ".dot" file is output to standard out. To convert it to a pdf, store it | 10 * The ".dot" file is output to standard out. To convert it to a pdf, store it |
| 11 * in a file (e.g. "tasks.dot"), and post-process it with | 11 * in a file (e.g. "tasks.dot"), and post-process it with |
| 12 * "dot tasks.dart -Tpdf -O". | 12 * "dot tasks.dot -Tpdf -O". |
| 13 * | 13 * |
| 14 * TODO(paulberry): | 14 * TODO(paulberry): |
| 15 * - Add general.dart and html.dart for completeness. | 15 * - Add general.dart and html.dart for completeness. |
| 16 * - Use Graphviz's "record" feature to produce more compact output | 16 * - Use Graphviz's "record" feature to produce more compact output |
| 17 * (http://www.graphviz.org/content/node-shapes#record) | 17 * (http://www.graphviz.org/content/node-shapes#record) |
| 18 * - Produce a warning if a result descriptor is found which isn't the output | 18 * - Produce a warning if a result descriptor is found which isn't the output |
| 19 * of exactly one task. | 19 * of exactly one task. |
| 20 * - Convert this tool to use package_config to find the package map. | 20 * - Convert this tool to use package_config to find the package map. |
| 21 */ | 21 */ |
| 22 library task_dependency_graph; | 22 library task_dependency_graph; |
| 23 | 23 |
| 24 import 'dart:io' hide File; | 24 import 'dart:io' hide File; |
| 25 | 25 |
| 26 import 'package:analyzer/analyzer.dart'; | 26 import 'package:analyzer/analyzer.dart'; |
| 27 import 'package:analyzer/file_system/file_system.dart'; | 27 import 'package:analyzer/file_system/file_system.dart'; |
| 28 import 'package:analyzer/file_system/physical_file_system.dart'; | 28 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 29 import 'package:analyzer/src/generated/constant.dart'; |
| 29 import 'package:analyzer/src/generated/element.dart'; | 30 import 'package:analyzer/src/generated/element.dart'; |
| 30 import 'package:analyzer/src/generated/engine.dart'; | 31 import 'package:analyzer/src/generated/engine.dart'; |
| 31 import 'package:analyzer/src/generated/java_io.dart'; | 32 import 'package:analyzer/src/generated/java_io.dart'; |
| 32 import 'package:analyzer/src/generated/sdk.dart'; | 33 import 'package:analyzer/src/generated/sdk.dart'; |
| 33 import 'package:analyzer/src/generated/sdk_io.dart'; | 34 import 'package:analyzer/src/generated/sdk_io.dart'; |
| 34 import 'package:analyzer/src/generated/source.dart'; | 35 import 'package:analyzer/src/generated/source.dart'; |
| 35 import 'package:analyzer/src/generated/source_io.dart'; | 36 import 'package:analyzer/src/generated/source_io.dart'; |
| 36 import 'package:path/path.dart' as path; | 37 import 'package:path/path.dart' as path; |
| 37 | 38 |
| 38 main() { | 39 main() { |
| 39 new Driver().run(); | 40 new Driver().run(); |
| 40 } | 41 } |
| 41 | 42 |
| 42 typedef void ResultDescriptorFinderCallback(PropertyAccessorElement element); | 43 typedef void GetterFinderCallback(PropertyAccessorElement element); |
| 43 | 44 |
| 44 class Driver { | 45 class Driver { |
| 45 PhysicalResourceProvider resourceProvider; | 46 PhysicalResourceProvider resourceProvider; |
| 46 AnalysisContext context; | 47 AnalysisContext context; |
| 47 InterfaceType resultDescriptorType; | 48 InterfaceType resultDescriptorType; |
| 49 InterfaceType listOfResultDescriptorType; |
| 50 ClassElement enginePluginClass; |
| 51 CompilationUnitElement taskUnitElement; |
| 52 InterfaceType extensionPointIdType; |
| 48 String rootDir; | 53 String rootDir; |
| 49 | 54 |
| 55 /** |
| 56 * Starting at [node], find all calls to registerExtension() which refer to |
| 57 * the given [extensionIdVariable], and execute [callback] for the associated |
| 58 * result descriptors. |
| 59 */ |
| 60 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable, |
| 61 void callback(descriptorName)) { |
| 62 Set<PropertyAccessorElement> resultDescriptors = |
| 63 new Set<PropertyAccessorElement>(); |
| 64 node.accept(new ExtensionFinder( |
| 65 resultDescriptorType, extensionIdVariable, resultDescriptors.add)); |
| 66 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { |
| 67 callback(resultDescriptor.name); |
| 68 } |
| 69 } |
| 70 |
| 71 /** |
| 72 * Starting at [node], find all references to a getter of type |
| 73 * `List<ResultDescriptor>`, and execute [callback] on the getter names. |
| 74 */ |
| 75 void findResultDescriptorLists( |
| 76 AstNode node, void callback(String descriptorListName)) { |
| 77 Set<PropertyAccessorElement> resultDescriptorLists = |
| 78 new Set<PropertyAccessorElement>(); |
| 79 node.accept(new GetterFinder( |
| 80 listOfResultDescriptorType, resultDescriptorLists.add)); |
| 81 for (PropertyAccessorElement resultDescriptorList |
| 82 in resultDescriptorLists) { |
| 83 // We only care about result descriptor lists associated with getters in |
| 84 // the engine plugin class. |
| 85 if (resultDescriptorList.enclosingElement != enginePluginClass) { |
| 86 continue; |
| 87 } |
| 88 callback(resultDescriptorList.name); |
| 89 } |
| 90 } |
| 91 |
| 50 void findResultDescriptors( | 92 void findResultDescriptors( |
| 51 AstNode node, void callback(String descriptorName)) { | 93 AstNode node, void callback(String descriptorName)) { |
| 52 Set<PropertyAccessorElement> resultDescriptors = | 94 Set<PropertyAccessorElement> resultDescriptors = |
| 53 new Set<PropertyAccessorElement>(); | 95 new Set<PropertyAccessorElement>(); |
| 54 node.accept(new ResultDescriptorFinder( | 96 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add)); |
| 55 resultDescriptorType, resultDescriptors.add)); | |
| 56 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { | 97 for (PropertyAccessorElement resultDescriptor in resultDescriptors) { |
| 57 callback(resultDescriptor.name); | 98 callback(resultDescriptor.name); |
| 58 } | 99 } |
| 59 } | 100 } |
| 60 | 101 |
| 61 /** | 102 /** |
| 62 * Find the root directory of the analyzer package by proceeding | 103 * Find the root directory of the analyzer package by proceeding |
| 63 * upward to the 'tool' dir, and then going up one more directory. | 104 * upward to the 'tool' dir, and then going up one more directory. |
| 64 */ | 105 */ |
| 65 String findRoot(String pathname) { | 106 String findRoot(String pathname) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 81 resourceProvider = PhysicalResourceProvider.INSTANCE; | 122 resourceProvider = PhysicalResourceProvider.INSTANCE; |
| 82 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; | 123 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; |
| 83 context = AnalysisEngine.instance.createAnalysisContext(); | 124 context = AnalysisEngine.instance.createAnalysisContext(); |
| 84 JavaFile packagesDir = new JavaFile(path.join(rootDir, 'packages')); | 125 JavaFile packagesDir = new JavaFile(path.join(rootDir, 'packages')); |
| 85 List<UriResolver> uriResolvers = [ | 126 List<UriResolver> uriResolvers = [ |
| 86 new DartUriResolver(sdk), | 127 new DartUriResolver(sdk), |
| 87 new PackageUriResolver(<JavaFile>[packagesDir]), | 128 new PackageUriResolver(<JavaFile>[packagesDir]), |
| 88 new FileUriResolver() | 129 new FileUriResolver() |
| 89 ]; | 130 ]; |
| 90 context.sourceFactory = new SourceFactory(uriResolvers); | 131 context.sourceFactory = new SourceFactory(uriResolvers); |
| 91 Source taskSource = | 132 Source dartDartSource = |
| 92 setupSource(path.join('lib', 'src', 'task', 'dart.dart')); | 133 setupSource(path.join('lib', 'src', 'task', 'dart.dart')); |
| 134 Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart')); |
| 93 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart')); | 135 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart')); |
| 136 Source enginePluginSource = |
| 137 setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart')); |
| 94 CompilationUnitElement modelElement = getUnit(modelSource).element; | 138 CompilationUnitElement modelElement = getUnit(modelSource).element; |
| 95 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type; | 139 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type; |
| 96 DartType dynamicType = context.typeProvider.dynamicType; | 140 DartType dynamicType = context.typeProvider.dynamicType; |
| 97 resultDescriptorType = modelElement | 141 resultDescriptorType = modelElement |
| 98 .getType('ResultDescriptor') | 142 .getType('ResultDescriptor') |
| 99 .type | 143 .type |
| 100 .substitute4([dynamicType]); | 144 .substitute4([dynamicType]); |
| 145 listOfResultDescriptorType = |
| 146 context.typeProvider.listType.substitute4([resultDescriptorType]); |
| 147 CompilationUnitElement enginePluginUnitElement = |
| 148 getUnit(enginePluginSource).element; |
| 149 enginePluginClass = enginePluginUnitElement.getType('EnginePlugin'); |
| 150 extensionPointIdType = |
| 151 enginePluginUnitElement.getType('ExtensionPointId').type; |
| 152 CompilationUnit dartDartUnit = getUnit(dartDartSource); |
| 153 CompilationUnitElement dartDartUnitElement = dartDartUnit.element; |
| 101 CompilationUnit taskUnit = getUnit(taskSource); | 154 CompilationUnit taskUnit = getUnit(taskSource); |
| 102 CompilationUnitElement taskUnitElement = taskUnit.element; | 155 taskUnitElement = taskUnit.element; |
| 103 print('digraph G {'); | 156 print('digraph G {'); |
| 104 Set<String> results = new Set<String>(); | 157 Set<String> results = new Set<String>(); |
| 105 for (ClassElement cls in taskUnitElement.types) { | 158 Set<String> resultLists = new Set<String>(); |
| 159 for (ClassElement cls in dartDartUnitElement.types) { |
| 106 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) { | 160 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) { |
| 107 String task = cls.name; | 161 String task = cls.name; |
| 108 // TODO(paulberry): node is deprecated. What am I supposed to do | 162 // TODO(paulberry): node is deprecated. What am I supposed to do |
| 109 // instead? | 163 // instead? |
| 110 findResultDescriptors(cls.getMethod('buildInputs').node, | 164 AstNode buildInputsAst = cls.getMethod('buildInputs').node; |
| 111 (String input) { | 165 findResultDescriptors(buildInputsAst, (String input) { |
| 112 results.add(input); | 166 results.add(input); |
| 113 print(' $input -> $task'); | 167 print(' $input -> $task'); |
| 114 }); | 168 }); |
| 169 findResultDescriptorLists(buildInputsAst, (String input) { |
| 170 resultLists.add(input); |
| 171 print(' $input -> $task'); |
| 172 }); |
| 115 findResultDescriptors(cls.getField('DESCRIPTOR').node, (String output) { | 173 findResultDescriptors(cls.getField('DESCRIPTOR').node, (String output) { |
| 116 results.add(output); | 174 results.add(output); |
| 117 print(' $task -> $output'); | 175 print(' $task -> $output'); |
| 118 }); | 176 }); |
| 119 } | 177 } |
| 120 } | 178 } |
| 179 AstNode enginePluginAst = enginePluginUnitElement.node; |
| 180 for (String resultList in resultLists) { |
| 181 print(' $resultList [shape=hexagon]'); |
| 182 TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList); |
| 183 findExtensions(enginePluginAst, extensionIdVariable, (String extension) { |
| 184 results.add(extension); |
| 185 print(' $extension -> $resultList'); |
| 186 }); |
| 187 } |
| 121 for (String result in results) { | 188 for (String result in results) { |
| 122 print(' $result [shape=box]'); | 189 print(' $result [shape=box]'); |
| 123 } | 190 } |
| 124 print('}'); | 191 print('}'); |
| 125 } | 192 } |
| 126 | 193 |
| 127 Source setupSource(String filename) { | 194 Source setupSource(String filename) { |
| 128 String filePath = path.join(rootDir, filename); | 195 String filePath = path.join(rootDir, filename); |
| 129 File file = resourceProvider.getResource(filePath); | 196 File file = resourceProvider.getResource(filePath); |
| 130 Source source = file.createSource(); | 197 Source source = file.createSource(); |
| 131 Uri restoredUri = context.sourceFactory.restoreUri(source); | 198 Uri restoredUri = context.sourceFactory.restoreUri(source); |
| 132 if (restoredUri != null) { | 199 if (restoredUri != null) { |
| 133 source = file.createSource(restoredUri); | 200 source = file.createSource(restoredUri); |
| 134 } | 201 } |
| 135 ChangeSet changeSet = new ChangeSet(); | 202 ChangeSet changeSet = new ChangeSet(); |
| 136 changeSet.addedSource(source); | 203 changeSet.addedSource(source); |
| 137 context.applyChanges(changeSet); | 204 context.applyChanges(changeSet); |
| 138 return source; | 205 return source; |
| 139 } | 206 } |
| 207 |
| 208 /** |
| 209 * Find the result list getter having name [resultListGetterName] in the |
| 210 * [EnginePlugin] class, and use the [ExtensionPointId] annotation on that |
| 211 * getter to find the associated [TopLevelVariableElement] which can be used |
| 212 * to register extensions for that getter. |
| 213 */ |
| 214 TopLevelVariableElement _getExtensionId(String resultListGetterName) { |
| 215 PropertyAccessorElement getter = |
| 216 enginePluginClass.getGetter(resultListGetterName); |
| 217 for (ElementAnnotation annotation in getter.metadata) { |
| 218 // TODO(paulberry): we should be using part of the public API rather than |
| 219 // just casting to ElementAnnotationImpl. |
| 220 ElementAnnotationImpl annotationImpl = annotation; |
| 221 DartObjectImpl annotationValue = annotationImpl.evaluationResult.value; |
| 222 if (annotationValue.type.isSubtypeOf(extensionPointIdType)) { |
| 223 String extensionPointId = |
| 224 annotationValue.fields['extensionPointId'].value; |
| 225 for (TopLevelVariableElement variable |
| 226 in taskUnitElement.topLevelVariables) { |
| 227 if (variable.name == extensionPointId) { |
| 228 return variable; |
| 229 } |
| 230 } |
| 231 } |
| 232 } |
| 233 throw new Exception( |
| 234 'Could not find extension ID corresponding to $resultListGetterName'); |
| 235 } |
| 140 } | 236 } |
| 141 | 237 |
| 142 class ResultDescriptorFinder extends GeneralizingAstVisitor { | 238 /** |
| 239 * Visitor that finds calls that register extension points. Specifically, we |
| 240 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`, |
| 241 * where `resultDescriptor` has type [resultDescriptorType], and we pass the |
| 242 * corresponding result descriptor names to [callback]. |
| 243 */ |
| 244 class ExtensionFinder extends GeneralizingAstVisitor { |
| 143 final InterfaceType resultDescriptorType; | 245 final InterfaceType resultDescriptorType; |
| 144 final ResultDescriptorFinderCallback callback; | 246 final TopLevelVariableElement extensionIdVariable; |
| 247 final GetterFinderCallback callback; |
| 145 | 248 |
| 146 ResultDescriptorFinder(this.resultDescriptorType, this.callback); | 249 ExtensionFinder( |
| 250 this.resultDescriptorType, this.extensionIdVariable, this.callback); |
| 147 | 251 |
| 148 @override | 252 @override |
| 149 visitIdentifier(Identifier node) { | 253 visitIdentifier(Identifier node) { |
| 150 Element element = node.staticElement; | 254 Element element = node.staticElement; |
| 151 if (element is PropertyAccessorElement && | 255 if (element is PropertyAccessorElement && |
| 152 element.isGetter && | 256 element.isGetter && |
| 153 element.returnType.isSubtypeOf(resultDescriptorType)) { | 257 element.variable == extensionIdVariable) { |
| 258 AstNode parent = node.parent; |
| 259 if (parent is ArgumentList && |
| 260 parent.arguments.length == 2 && |
| 261 parent.arguments[0] == node) { |
| 262 Expression extension = parent.arguments[1]; |
| 263 if (extension is Identifier) { |
| 264 Element element = extension.staticElement; |
| 265 if (element is PropertyAccessorElement && |
| 266 element.isGetter && |
| 267 element.returnType.isSubtypeOf(resultDescriptorType)) { |
| 268 callback(element); |
| 269 return; |
| 270 } |
| 271 } |
| 272 } |
| 273 throw new Exception('Could not decode extension setup: $parent'); |
| 274 } |
| 275 } |
| 276 } |
| 277 |
| 278 /** |
| 279 * Visitor that finds references to getters having a specific type (or a |
| 280 * subtype of that type) |
| 281 */ |
| 282 class GetterFinder extends GeneralizingAstVisitor { |
| 283 final InterfaceType type; |
| 284 final GetterFinderCallback callback; |
| 285 |
| 286 GetterFinder(this.type, this.callback); |
| 287 |
| 288 @override |
| 289 visitIdentifier(Identifier node) { |
| 290 Element element = node.staticElement; |
| 291 if (element is PropertyAccessorElement && |
| 292 element.isGetter && |
| 293 element.returnType.isSubtypeOf(type)) { |
| 154 callback(element); | 294 callback(element); |
| 155 } | 295 } |
| 156 } | 296 } |
| 157 } | 297 } |
| OLD | NEW |