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

Side by Side 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 unified diff | Download patch
OLDNEW
(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 /**
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
8 * get" in the analyzer directory to ensure that a "packages" folder exists.
9 *
10 * TODO(paulberry):
11 * - Add general.dart and html.dart for completeness.
12 * - Use Graphviz's "record" feature to produce more compact output
13 * (http://www.graphviz.org/content/node-shapes#record)
14 * - Produce a warning if a result descriptor is found which isn't the output
15 * of exactly one task.
16 * - Convert this tool to use package_config to find the package map.
17 */
18 library task_dependency_graph.generate;
19
20 import 'dart:io' hide File;
21 import 'dart:io' as io;
22
23 import 'package:analyzer/analyzer.dart';
24 import 'package:analyzer/file_system/file_system.dart';
25 import 'package:analyzer/file_system/physical_file_system.dart';
26 import 'package:analyzer/src/generated/constant.dart';
27 import 'package:analyzer/src/generated/element.dart';
28 import 'package:analyzer/src/generated/engine.dart';
29 import 'package:analyzer/src/generated/java_io.dart';
30 import 'package:analyzer/src/generated/sdk.dart';
31 import 'package:analyzer/src/generated/sdk_io.dart';
32 import 'package:analyzer/src/generated/source.dart';
33 import 'package:analyzer/src/generated/source_io.dart';
34 import 'package:path/path.dart' as path;
35
36 /**
37 * Generate the target .dot file.
38 */
39 main() {
40 new Driver().generateFile();
41 }
42
43 typedef void GetterFinderCallback(PropertyAccessorElement element);
44
45 class Driver {
46 PhysicalResourceProvider resourceProvider;
47 AnalysisContext context;
48 InterfaceType resultDescriptorType;
49 InterfaceType listOfResultDescriptorType;
50 ClassElement enginePluginClass;
51 CompilationUnitElement taskUnitElement;
52 InterfaceType extensionPointIdType;
53 final String rootDir;
54
55 Driver()
56 : rootDir =
57 findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
58
59 /**
60 * Get an [io.File] object corresponding to the file in which the generated
61 * graph should be output.
62 */
63 io.File get file => new io.File(
64 path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot'));
65
66 /**
67 * Determine if the output [file] contains the expected contents.
68 */
69 bool checkFile() {
70 String expectedContents = generateFileContents();
71 String actualContents = file.readAsStringSync();
72 // Normalize Windows line endings to Unix line endings so that the
73 // comparison doesn't fail on Windows.
74 actualContents = actualContents.replaceAll('\r\n', '\n');
75 return expectedContents == actualContents;
76 }
77
78 /**
79 * Starting at [node], find all calls to registerExtension() which refer to
80 * the given [extensionIdVariable], and execute [callback] for the associated
81 * result descriptors.
82 */
83 void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable,
84 void callback(descriptorName)) {
85 Set<PropertyAccessorElement> resultDescriptors =
86 new Set<PropertyAccessorElement>();
87 node.accept(new ExtensionFinder(
88 resultDescriptorType, extensionIdVariable, resultDescriptors.add));
89 for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
90 callback(resultDescriptor.name);
91 }
92 }
93
94 /**
95 * Starting at [node], find all references to a getter of type
96 * `List<ResultDescriptor>`, and execute [callback] on the getter names.
97 */
98 void findResultDescriptorLists(
99 AstNode node, void callback(String descriptorListName)) {
100 Set<PropertyAccessorElement> resultDescriptorLists =
101 new Set<PropertyAccessorElement>();
102 node.accept(new GetterFinder(
103 listOfResultDescriptorType, resultDescriptorLists.add));
104 for (PropertyAccessorElement resultDescriptorList
105 in resultDescriptorLists) {
106 // We only care about result descriptor lists associated with getters in
107 // the engine plugin class.
108 if (resultDescriptorList.enclosingElement != enginePluginClass) {
109 continue;
110 }
111 callback(resultDescriptorList.name);
112 }
113 }
114
115 void findResultDescriptors(
116 AstNode node, void callback(String descriptorName)) {
117 Set<PropertyAccessorElement> resultDescriptors =
118 new Set<PropertyAccessorElement>();
119 node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add));
120 for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
121 callback(resultDescriptor.name);
122 }
123 }
124
125 /**
126 * Generate the task dependency graph and write it to the output [file].
127 */
128 void generateFile() {
129 String fileContents = generateFileContents();
130 file.writeAsStringSync(fileContents);
131 }
132
133 /**
134 * Generate the task dependency graph and return it as a [String].
135 */
136 String generateFileContents() {
137 List<String> lines = <String>[];
138 resourceProvider = PhysicalResourceProvider.INSTANCE;
139 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
140 context = AnalysisEngine.instance.createAnalysisContext();
141 String packageRootPath;
142 if (Platform.packageRoot.isNotEmpty) {
143 packageRootPath = Platform.packageRoot;
144 } else {
145 packageRootPath = path.join(rootDir, 'packages');
146 }
147 JavaFile packagesDir = new JavaFile(packageRootPath);
148 List<UriResolver> uriResolvers = [
149 new DartUriResolver(sdk),
150 new PackageUriResolver(<JavaFile>[packagesDir]),
151 new FileUriResolver()
152 ];
153 context.sourceFactory = new SourceFactory(uriResolvers);
154 Source dartDartSource =
155 setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
156 Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart'));
157 Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
158 Source enginePluginSource =
159 setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart'));
160 CompilationUnitElement modelElement = getUnit(modelSource).element;
161 InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
162 DartType dynamicType = context.typeProvider.dynamicType;
163 resultDescriptorType = modelElement
164 .getType('ResultDescriptor')
165 .type
166 .substitute4([dynamicType]);
167 listOfResultDescriptorType =
168 context.typeProvider.listType.substitute4([resultDescriptorType]);
169 CompilationUnitElement enginePluginUnitElement =
170 getUnit(enginePluginSource).element;
171 enginePluginClass = enginePluginUnitElement.getType('EnginePlugin');
172 extensionPointIdType =
173 enginePluginUnitElement.getType('ExtensionPointId').type;
174 CompilationUnit dartDartUnit = getUnit(dartDartSource);
175 CompilationUnitElement dartDartUnitElement = dartDartUnit.element;
176 CompilationUnit taskUnit = getUnit(taskSource);
177 taskUnitElement = taskUnit.element;
178 Set<String> results = new Set<String>();
179 Set<String> resultLists = new Set<String>();
180 for (ClassElement cls in dartDartUnitElement.types) {
181 if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
182 String task = cls.name;
183 AstNode buildInputsAst = cls.getMethod('buildInputs').computeNode();
184 findResultDescriptors(buildInputsAst, (String input) {
185 results.add(input);
186 lines.add(' $input -> $task');
187 });
188 findResultDescriptorLists(buildInputsAst, (String input) {
189 resultLists.add(input);
190 lines.add(' $input -> $task');
191 });
192 findResultDescriptors(cls.getField('DESCRIPTOR').computeNode(), (String out) {
193 results.add(out);
194 lines.add(' $task -> $out');
195 });
196 }
197 }
198 AstNode enginePluginAst = enginePluginUnitElement.computeNode();
199 for (String resultList in resultLists) {
200 lines.add(' $resultList [shape=hexagon]');
201 TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList);
202 findExtensions(enginePluginAst, extensionIdVariable, (String extension) {
203 results.add(extension);
204 lines.add(' $extension -> $resultList');
205 });
206 }
207 for (String result in results) {
208 lines.add(' $result [shape=box]');
209 }
210 lines.sort();
211 return '''
212 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
213 // for details. All rights reserved. Use of this source code is governed by a
214 // BSD-style license that can be found in the LICENSE file.
215 //
216 // This file has been automatically generated. Please do not edit it manually.
217 // To regenerate the file, use the script
218 // "pkg/analyzer/tool/task_dependency_graph/generate.dart".
219 //
220 // To render this graph using Graphviz (www.graphviz.org) use the command:
221 // "dot tasks.dot -Tpdf -O".
222 digraph G {
223 ${lines.join('\n')}
224 }
225 ''';
226 }
227
228 CompilationUnit getUnit(Source source) =>
229 context.resolveCompilationUnit2(source, source);
230
231 Source setupSource(String filename) {
232 String filePath = path.join(rootDir, filename);
233 File file = resourceProvider.getResource(filePath);
234 Source source = file.createSource();
235 Uri restoredUri = context.sourceFactory.restoreUri(source);
236 if (restoredUri != null) {
237 source = file.createSource(restoredUri);
238 }
239 ChangeSet changeSet = new ChangeSet();
240 changeSet.addedSource(source);
241 context.applyChanges(changeSet);
242 return source;
243 }
244
245 /**
246 * Find the result list getter having name [resultListGetterName] in the
247 * [EnginePlugin] class, and use the [ExtensionPointId] annotation on that
248 * getter to find the associated [TopLevelVariableElement] which can be used
249 * to register extensions for that getter.
250 */
251 TopLevelVariableElement _getExtensionId(String resultListGetterName) {
252 PropertyAccessorElement getter =
253 enginePluginClass.getGetter(resultListGetterName);
254 for (ElementAnnotation annotation in getter.metadata) {
255 DartObjectImpl annotationValue = annotation.constantValue;
256 if (annotationValue.type.isSubtypeOf(extensionPointIdType)) {
257 String extensionPointId =
258 annotationValue.fields['extensionPointId'].toStringValue();
259 for (TopLevelVariableElement variable
260 in taskUnitElement.topLevelVariables) {
261 if (variable.name == extensionPointId) {
262 return variable;
263 }
264 }
265 }
266 }
267 throw new Exception(
268 'Could not find extension ID corresponding to $resultListGetterName');
269 }
270
271 /**
272 * Find the root directory of the analyzer package by proceeding
273 * upward to the 'tool' dir, and then going up one more directory.
274 */
275 static String findRoot(String pathname) {
276 while (path.basename(pathname) != 'tool') {
277 String parent = path.dirname(pathname);
278 if (parent.length >= pathname.length) {
279 throw new Exception("Can't find root directory");
280 }
281 pathname = parent;
282 }
283 return path.dirname(pathname);
284 }
285 }
286
287 /**
288 * Visitor that finds calls that register extension points. Specifically, we
289 * look for calls of the form `method(extensionIdVariable, resultDescriptor)`,
290 * where `resultDescriptor` has type [resultDescriptorType], and we pass the
291 * corresponding result descriptor names to [callback].
292 */
293 class ExtensionFinder extends GeneralizingAstVisitor {
294 final InterfaceType resultDescriptorType;
295 final TopLevelVariableElement extensionIdVariable;
296 final GetterFinderCallback callback;
297
298 ExtensionFinder(
299 this.resultDescriptorType, this.extensionIdVariable, this.callback);
300
301 @override
302 visitIdentifier(Identifier node) {
303 Element element = node.staticElement;
304 if (element is PropertyAccessorElement &&
305 element.isGetter &&
306 element.variable == extensionIdVariable) {
307 AstNode parent = node.parent;
308 if (parent is ArgumentList &&
309 parent.arguments.length == 2 &&
310 parent.arguments[0] == node) {
311 Expression extension = parent.arguments[1];
312 if (extension is Identifier) {
313 Element element = extension.staticElement;
314 if (element is PropertyAccessorElement &&
315 element.isGetter &&
316 element.returnType.isSubtypeOf(resultDescriptorType)) {
317 callback(element);
318 return;
319 }
320 }
321 }
322 throw new Exception('Could not decode extension setup: $parent');
323 }
324 }
325 }
326
327 /**
328 * Visitor that finds references to getters having a specific type (or a
329 * subtype of that type)
330 */
331 class GetterFinder extends GeneralizingAstVisitor {
332 final InterfaceType type;
333 final GetterFinderCallback callback;
334
335 GetterFinder(this.type, this.callback);
336
337 @override
338 visitIdentifier(Identifier node) {
339 Element element = node.staticElement;
340 if (element is PropertyAccessorElement &&
341 element.isGetter &&
342 element.returnType.isSubtypeOf(type)) {
343 callback(element);
344 }
345 }
346 }
OLDNEW
« 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