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

Side by Side Diff: pkg/analyzer2dart/lib/src/tree_shaker.dart

Issue 2037123002: Cleanup: remove package "analyzer2dart". (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 6 months 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
« no previous file with comments | « pkg/analyzer2dart/lib/src/semantic_visitor.dart ('k') | pkg/analyzer2dart/lib/src/util.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014, 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 analyzer2dart.treeShaker;
6
7 import 'dart:collection';
8
9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/src/generated/element.dart';
11 import 'package:analyzer/src/generated/source.dart';
12 import 'package:analyzer/src/generated/resolver.dart';
13 import 'package:compiler/src/universe/universe.dart';
14
15 import 'closed_world.dart';
16 import 'util.dart';
17 import 'semantic_visitor.dart';
18 import 'identifier_semantics.dart';
19
20 /**
21 * The result of performing local reachability analysis on a method.
22 */
23 class MethodAnalysis {
24 /**
25 * The AST for the method.
26 */
27 final Declaration declaration;
28
29 /**
30 * The functions statically called by the method.
31 */
32 final List<ExecutableElement> calls = <ExecutableElement>[];
33
34 /**
35 * The fields and top-level variables statically accessed by the method.
36 */
37 // TODO(johnniwinther): Should we split this into reads and writes?
38 final List<PropertyInducingElement> accesses = <PropertyInducingElement>[];
39
40 /**
41 * The selectors used by the method to perform dynamic invocation.
42 */
43 final List<Selector> invokes = <Selector>[];
44
45 /**
46 * The classes that are instantiated by the method.
47 */
48 // TODO(johnniwinther,paulberry): Register instantiated types.
49 // TODO(johnniwinther,paulberry): Register checked types from is/as checks,
50 // catch clauses and (checked) type annotations.
51 final List<ClassElement> instantiates = <ClassElement>[];
52
53 MethodAnalysis(this.declaration);
54 }
55
56 /**
57 * The result of performing local reachability analysis on a class.
58 *
59 * TODO(paulberry): Do we need to do any other analysis of classes? (For
60 * example, detect annotations that are relevant to mirrors, detect that a
61 * class might be used for custom HTML elements, or collect inherited and
62 * mixed-in classes).
63 */
64 class ClassAnalysis {
65 /**
66 * The AST for the class.
67 */
68 final ClassDeclaration declaration;
69
70 ClassAnalysis(this.declaration);
71 }
72
73 /**
74 * This class is responsible for performing local analysis of the source code
75 * to provide the information needed to do tree shaking.
76 */
77 class LocalReachabilityComputer {
78 /**
79 * Perform local reachability analysis of [method].
80 */
81 MethodAnalysis analyzeMethod(ExecutableElement method) {
82 Declaration declaration = method.node;
83 MethodAnalysis analysis = new MethodAnalysis(declaration);
84 if (declaration != null) {
85 declaration.accept(new TreeShakingVisitor(analysis));
86 } else if (method is ConstructorElement) {
87 // This constructor has no associated declaration in the AST. Either it
88 // is a default constructor for an ordinary class, or it's a synthetic
89 // constructor associated with a mixin. For now we assume it's a default
90 // constructor, in which case all we need to do is record the class as
91 // being instantiated by this method. TODO(paulberry): handle the
92 // mixin case.
93 ClassElement instantiatedClass = method.enclosingElement;
94 analysis.instantiates.add(instantiatedClass);
95 if (instantiatedClass.supertype != null) {
96 ClassElement superClass = instantiatedClass.supertype.element;
97 ConstructorElement superConstructor = superClass.unnamedConstructor;
98 if (superConstructor != null) {
99 // TODO(johnniwinther): Register instantiated type and selector.
100 analysis.calls.add(superConstructor);
101 }
102 }
103 } else {
104 // This is an executable element with no associated declaration in the
105 // AST, and it's not a constructor. TODO(paulberry): can this ever
106 // happen?
107 throw new UnimplementedError();
108 }
109 return analysis;
110 }
111
112 /**
113 * Perform local reachability analysis of [classElement].
114 */
115 ClassAnalysis analyzeClass(ClassElement classElement) {
116 return new ClassAnalysis(classElement.node);
117 }
118
119 /**
120 * Determine which members of [classElement] are matched by the given
121 * [selector].
122 *
123 * [methods] is populated with all the class methods which are matched by the
124 * selector, [accessors] with all the getters and setters which are matched
125 * by the selector, and [fields] with all the fields which are matched by the
126 * selector.
127 */
128 void getMatchingClassMembers(ClassElement classElement, Selector selector,
129 List<MethodElement> methods, List<PropertyAccessorElement> accessors,
130 List<PropertyInducingElement> fields) {
131 // TODO(paulberry): should we walk through superclasses and mixins as well
132 // here? Or would it be better to make [TreeShaker] responsible for those
133 // relationships (since they are non-local)? Consider making use of
134 // InheritanceManager to do this.
135 for (MethodElement method in classElement.methods) {
136 // TODO(paulberry): account for arity and named arguments when matching
137 // the selector against the method.
138 if (selector.name == method.name) {
139 methods.add(method);
140 }
141 }
142 if (selector.kind == SelectorKind.GETTER) {
143 for (PropertyAccessorElement accessor in classElement.accessors) {
144 if (accessor.isGetter && selector.name == accessor.name) {
145 if (accessor.isSynthetic) {
146 // This accessor is implied by the corresponding field declaration.
147 fields.add(accessor.variable);
148 } else {
149 accessors.add(accessor);
150 }
151 }
152 }
153 } else if (selector.kind == SelectorKind.SETTER) {
154 // accessor.name uses the convention that setter names end in '='.
155 String selectorNameWithEquals = '${selector.name}=';
156 for (PropertyAccessorElement accessor in classElement.accessors) {
157 if (accessor.isSetter && selectorNameWithEquals == accessor.name) {
158 if (accessor.isSynthetic) {
159 // This accessor is implied by the corresponding field declaration.
160 // TODO(paulberry): should we distinguish reads and writes?
161 fields.add(accessor.variable);
162 } else {
163 accessors.add(accessor);
164 }
165 }
166 }
167 }
168 }
169 }
170
171 /**
172 * This class is responsible for driving the tree shaking process, and
173 * and performing the global inferences necessary to determine which methods
174 * in the source program are reachable. It makes use of
175 * [LocalReachabilityComputer] to do local analysis of individual classes and
176 * methods.
177 */
178 class TreeShaker {
179 List<Element> _queue = <Element>[];
180 Set<Element> _alreadyEnqueued = new HashSet<Element>();
181 ClosedWorld _world;
182 Set<Selector> _selectors = new HashSet<Selector>();
183 final LocalReachabilityComputer _localComputer =
184 new LocalReachabilityComputer();
185
186 TreeShaker(TypeProvider typeProvider, FunctionElement mainFunction)
187 : _world = new ClosedWorld(typeProvider, mainFunction);
188
189 void _addElement(Element element) {
190 if (_alreadyEnqueued.add(element)) {
191 _queue.add(element);
192 }
193 }
194
195 void _addSelector(Selector selector) {
196 if (_selectors.add(selector)) {
197 // New selector, so match it against all class methods.
198 _world.instantiatedClasses.forEach((ClassElement element, AstNode node) {
199 _matchClassToSelector(element, selector);
200 });
201 }
202 }
203
204 void _matchClassToSelector(ClassElement classElement, Selector selector) {
205 List<MethodElement> methods = <MethodElement>[];
206 List<PropertyAccessorElement> accessors = <PropertyAccessorElement>[];
207 List<PropertyInducingElement> fields = <PropertyInducingElement>[];
208 _localComputer.getMatchingClassMembers(
209 classElement,
210 selector,
211 methods,
212 accessors,
213 fields);
214 methods.forEach(_addElement);
215 accessors.forEach(_addElement);
216 fields.forEach(_addElement);
217 }
218
219 ClosedWorld shake() {
220 _addElement(_world.mainFunction);
221 while (_queue.isNotEmpty) {
222 Element element = _queue.removeLast();
223 if (element is ExecutableElement) {
224 MethodAnalysis analysis = _localComputer.analyzeMethod(element);
225 _world.executableElements[element] = analysis.declaration;
226 analysis.calls.forEach(_addElement);
227 analysis.invokes.forEach(_addSelector);
228 analysis.instantiates.forEach(_addElement);
229 analysis.accesses.forEach(_addElement);
230 } else if (element is ClassElement) {
231 ClassAnalysis analysis = _localComputer.analyzeClass(element);
232 _world.instantiatedClasses[element] = analysis.declaration;
233 for (Selector selector in _selectors) {
234 _matchClassToSelector(element, selector);
235 }
236 } else if (element is FieldElement) {
237 VariableDeclaration declaration = element.node;
238 _world.fields[element] = declaration;
239 } else if (element is TopLevelVariableElement) {
240 VariableDeclaration declaration = element.node;
241 _world.variables[element] = declaration;
242 } else {
243 throw new Exception(
244 'Unexpected element type while tree shaking: '
245 '$element (${element.runtimeType})');
246 }
247 }
248 return _world;
249 }
250 }
251
252 class TreeShakingVisitor extends SemanticVisitor {
253 final MethodAnalysis analysis;
254
255 TreeShakingVisitor(this.analysis);
256
257 Source get currentSource => analysis.declaration.element.source;
258
259 @override
260 void visitInstanceCreationExpression(InstanceCreationExpression node) {
261 ConstructorElement staticElement = node.staticElement;
262 if (staticElement != null) {
263 analysis.calls.add(staticElement);
264 } else {
265 // TODO(paulberry): deal with this situation. This can happen, for
266 // example, in the case "main() => new Unresolved();" (which is a
267 // warning, not an error).
268 }
269 super.visitInstanceCreationExpression(node);
270 }
271
272 @override
273 void visitDynamicInvocation(MethodInvocation node,
274 AccessSemantics semantics) {
275 analysis.invokes.add(createSelectorFromMethodInvocation(
276 node.argumentList, node.methodName.name));
277 }
278
279 @override
280 void visitLocalFunctionInvocation(MethodInvocation node,
281 AccessSemantics semantics) {
282 // Locals don't need to be tree shaken.
283 }
284
285 @override
286 void visitLocalVariableInvocation(MethodInvocation node,
287 AccessSemantics semantics) {
288 // Locals don't need to be tree shaken.
289 }
290
291 @override
292 void visitParameterInvocation(MethodInvocation node,
293 AccessSemantics semantics) {
294 // Locals don't need to be tree shaken.
295 }
296
297 @override
298 void visitStaticFieldInvocation(MethodInvocation node,
299 AccessSemantics semantics) {
300 // Invocation of a static field.
301 analysis.accesses.add(semantics.element);
302 analysis.invokes.add(createSelectorFromMethodInvocation(
303 node.argumentList, 'call'));
304 }
305
306 @override
307 void visitStaticMethodInvocation(MethodInvocation node,
308 AccessSemantics semantics) {
309 analysis.calls.add(semantics.element);
310 }
311
312 @override
313 void visitStaticPropertyInvocation(MethodInvocation node,
314 AccessSemantics semantics) {
315 // Invocation of a property. TODO(paulberry): handle this.
316 super.visitStaticPropertyInvocation(node, semantics);
317 }
318
319 void handleDynamicAccess(AccessSemantics semantics) {
320 if (semantics.isRead) {
321 analysis.invokes.add(
322 new Selector.getter(semantics.identifier.name, null));
323 }
324 if (semantics.isWrite) {
325 // Selector.setter constructor uses the convention that setter names
326 // don't end in '='.
327 analysis.invokes.add(
328 new Selector.setter(semantics.identifier.name, null));
329 }
330 }
331
332 @override
333 void visitDynamicAccess(AstNode node, AccessSemantics semantics) {
334 handleDynamicAccess(semantics);
335 }
336
337 @override
338 void visitLocalFunctionAccess(AstNode node, AccessSemantics semantics) {
339 // Locals don't need to be tree shaken.
340 }
341
342 @override
343 void visitLocalVariableAccess(AstNode node, AccessSemantics semantics) {
344 // Locals don't need to be tree shaken.
345 }
346
347 @override
348 void visitParameterAccess(AstNode node, AccessSemantics semantics) {
349 // Locals don't need to be tree shaken.
350 }
351
352 @override
353 void visitStaticFieldAccess(AstNode node, AccessSemantics semantics) {
354 analysis.accesses.add(semantics.element);
355 }
356
357 @override
358 void visitStaticMethodAccess(AstNode node, AccessSemantics semantics) {
359 // Method tear-off. TODO(paulberry): implement.
360 super.visitStaticMethodAccess(node, semantics);
361 }
362
363 @override
364 void visitStaticPropertyAccess(AstNode node, AccessSemantics semantics) {
365 // TODO(paulberry): implement.
366 super.visitStaticPropertyAccess(node, semantics);
367 }
368
369 @override
370 void visitConstructorDeclaration(ConstructorDeclaration node) {
371 // TODO(paulberry): handle parameter list.
372 node.initializers.accept(this);
373 node.body.accept(this);
374 if (node.factoryKeyword == null) {
375 // This is a generative constructor. Figure out if it is redirecting.
376 // If it isn't, then the constructor instantiates the class so we need to
377 // add the class to analysis.instantiates. (If it is redirecting, then
378 // we don't need to, because the redirected-to constructor will take care
379 // of that).
380 if (node.initializers.length != 1 || node.initializers[0] is! RedirectingC onstructorInvocation) {
381 ClassElement classElement = node.element.enclosingElement;
382 analysis.instantiates.add(node.element.enclosingElement);
383 if (!node.initializers.any((i) => i is SuperConstructorInvocation)) {
384 if (classElement.supertype != null) {
385 ClassElement superClass = classElement.supertype.element;
386 ConstructorElement superConstructor = superClass.unnamedConstructor;
387 if (superConstructor != null) {
388 // TODO(johnniwinther): Register instantiated type and selector.
389 analysis.calls.add(superConstructor);
390 }
391 }
392 }
393 }
394 } else if (node.redirectedConstructor != null) {
395 if (node.redirectedConstructor.staticElement == null) {
396 // Factory constructor redirects to a non-existent constructor.
397 // TODO(paulberry): handle this.
398 throw new UnimplementedError();
399 } else {
400 analysis.calls.add(node.redirectedConstructor.staticElement);
401 }
402 }
403 }
404
405 @override
406 void
407 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation nod e) {
408 // Note: we don't have to worry about node.staticElement being
409 // null, because that would have been detected by the analyzer and
410 // reported as a compile time error.
411 analysis.calls.add(node.staticElement);
412 }
413
414 @override
415 void handleAssignmentExpression(AssignmentExpression node) {
416 // Don't special-case assignment expressions.
417 }
418 }
OLDNEW
« no previous file with comments | « pkg/analyzer2dart/lib/src/semantic_visitor.dart ('k') | pkg/analyzer2dart/lib/src/util.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698