Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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 library analyzer2dart.treeShaker; | 5 library analyzer2dart.treeShaker; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 | 68 |
| 69 /** | 69 /** |
| 70 * This class is responsible for performing local analysis of the source code | 70 * This class is responsible for performing local analysis of the source code |
| 71 * to provide the information needed to do tree shaking. | 71 * to provide the information needed to do tree shaking. |
| 72 */ | 72 */ |
| 73 class LocalReachabilityComputer { | 73 class LocalReachabilityComputer { |
| 74 /** | 74 /** |
| 75 * Perform local reachability analysis of [method]. | 75 * Perform local reachability analysis of [method]. |
| 76 */ | 76 */ |
| 77 MethodAnalysis analyzeMethod(ExecutableElement method) { | 77 MethodAnalysis analyzeMethod(ExecutableElement method) { |
| 78 MethodAnalysis analysis = new MethodAnalysis(method.node); | 78 Declaration declaration = method.node; |
| 79 analysis.declaration.accept(new TreeShakingVisitor(analysis)); | 79 MethodAnalysis analysis = new MethodAnalysis(declaration); |
| 80 if (declaration != null) { | |
| 81 declaration.accept(new TreeShakingVisitor(analysis)); | |
| 82 } else if (method is ConstructorElement) { | |
| 83 // This constructor has no associated declaration in the AST. Either it | |
| 84 // is a default constructor for an ordinary class, or it's a synthetic | |
| 85 // constructor associated with a mixin. For now we assume it's a default | |
| 86 // constructor, in which case all we need to do is record the class as | |
| 87 // being instantiated by this method. TODO(paulberry): handle the | |
| 88 // mixin case. | |
| 89 analysis.instantiates.add(method.enclosingElement); | |
| 90 } else { | |
| 91 // This is an executable element with no associated declaration in the | |
| 92 // AST, and it's not a constructor. TODO(paulberry): can this ever | |
| 93 // happen? | |
| 94 throw new UnimplementedError(); | |
| 95 } | |
| 80 return analysis; | 96 return analysis; |
| 81 } | 97 } |
| 82 | 98 |
| 83 /** | 99 /** |
| 84 * Perform local reachability analysis of [classElement]. | 100 * Perform local reachability analysis of [classElement]. |
| 85 */ | 101 */ |
| 86 ClassAnalysis analyzeClass(ClassElement classElement) { | 102 ClassAnalysis analyzeClass(ClassElement classElement) { |
| 87 return new ClassAnalysis(classElement.node); | 103 return new ClassAnalysis(classElement.node); |
| 88 } | 104 } |
| 89 | 105 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 130 * and performing the global inferences necessary to determine which methods | 146 * and performing the global inferences necessary to determine which methods |
| 131 * in the source program are reachable. It makes use of | 147 * in the source program are reachable. It makes use of |
| 132 * [LocalReachabilityComputer] to do local analysis of individual classes and | 148 * [LocalReachabilityComputer] to do local analysis of individual classes and |
| 133 * methods. | 149 * methods. |
| 134 */ | 150 */ |
| 135 class TreeShaker { | 151 class TreeShaker { |
| 136 List<Element> _queue = <Element>[]; | 152 List<Element> _queue = <Element>[]; |
| 137 Set<Element> _alreadyEnqueued = new HashSet<Element>(); | 153 Set<Element> _alreadyEnqueued = new HashSet<Element>(); |
| 138 ClosedWorld _world; | 154 ClosedWorld _world; |
| 139 Set<Selector> _selectors = new HashSet<Selector>(); | 155 Set<Selector> _selectors = new HashSet<Selector>(); |
| 140 final LocalReachabilityComputer _localComputer = new LocalReachabilityComputer (); | 156 final LocalReachabilityComputer _localComputer = |
| 157 new LocalReachabilityComputer(); | |
| 141 | 158 |
| 142 TreeShaker(FunctionElement mainFunction) | 159 TreeShaker(FunctionElement mainFunction) |
| 143 : _world = new ClosedWorld(mainFunction); | 160 : _world = new ClosedWorld(mainFunction); |
| 144 | 161 |
| 145 void _addElement(Element element) { | 162 void _addElement(Element element) { |
| 146 if (_alreadyEnqueued.add(element)) { | 163 if (_alreadyEnqueued.add(element)) { |
| 147 _queue.add(element); | 164 _queue.add(element); |
| 148 } | 165 } |
| 149 } | 166 } |
| 150 | 167 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 192 } | 209 } |
| 193 } else if (element is FieldElement) { | 210 } else if (element is FieldElement) { |
| 194 VariableDeclaration declaration = element.node; | 211 VariableDeclaration declaration = element.node; |
| 195 _world.fields[element] = declaration; | 212 _world.fields[element] = declaration; |
| 196 } else if (element is TopLevelVariableElement) { | 213 } else if (element is TopLevelVariableElement) { |
| 197 VariableDeclaration declaration = element.node; | 214 VariableDeclaration declaration = element.node; |
| 198 _world.variables[element] = declaration; | 215 _world.variables[element] = declaration; |
| 199 } else { | 216 } else { |
| 200 throw new Exception( | 217 throw new Exception( |
| 201 'Unexpected element type while tree shaking: ' | 218 'Unexpected element type while tree shaking: ' |
| 202 '$element (${element.runtimeType})'); | 219 '$element (${element.runtimeType})'); |
| 203 } | 220 } |
| 204 } | 221 } |
| 205 print('Tree shaking done'); | 222 print('Tree shaking done'); |
| 206 return _world; | 223 return _world; |
| 207 } | 224 } |
| 208 } | 225 } |
| 209 | 226 |
| 210 class TreeShakingVisitor extends SemanticVisitor { | 227 class TreeShakingVisitor extends SemanticVisitor { |
| 211 final MethodAnalysis analysis; | 228 final MethodAnalysis analysis; |
| 212 | 229 |
| 213 TreeShakingVisitor(this.analysis); | 230 TreeShakingVisitor(this.analysis); |
| 214 | 231 |
| 215 Source get currentSource => analysis.declaration.element.source; | 232 Source get currentSource => analysis.declaration.element.source; |
| 216 | 233 |
| 217 @override | 234 @override |
| 218 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 235 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 219 ConstructorElement staticElement = node.staticElement; | 236 ConstructorElement staticElement = node.staticElement; |
| 220 if (staticElement != null) { | 237 if (staticElement != null) { |
| 221 // TODO(paulberry): Really we should enqueue the constructor, and then | 238 analysis.calls.add(staticElement); |
| 222 // when we visit it add the class to the class bucket. | |
| 223 ClassElement classElement = staticElement.enclosingElement; | |
| 224 analysis.instantiates.add(classElement); | |
| 225 } else { | 239 } else { |
| 226 // TODO(paulberry): deal with this situation. This can happen, for | 240 // TODO(paulberry): deal with this situation. This can happen, for |
| 227 // example, in the case "main() => new Unresolved();" (which is a | 241 // example, in the case "main() => new Unresolved();" (which is a |
| 228 // warning, not an error). | 242 // warning, not an error). |
| 229 } | 243 } |
| 230 super.visitInstanceCreationExpression(node); | 244 super.visitInstanceCreationExpression(node); |
| 231 } | 245 } |
| 232 | 246 |
| 233 @override | 247 @override |
| 234 void visitDynamicInvocation(MethodInvocation node, | 248 void visitDynamicInvocation(MethodInvocation node, |
| 235 AccessSemantics semantics) { | 249 AccessSemantics semantics) { |
| 236 analysis.invokes.add( | 250 analysis.invokes.add( |
| 237 createSelectorFromMethodInvocation(node, node.methodName.name)); | 251 createSelectorFromMethodInvocation(node, node.methodName.name)); |
| 238 } | 252 } |
| 239 | 253 |
| 240 @override | 254 @override |
| 241 void visitLocalFunctionInvocation(MethodInvocation node, | 255 void visitLocalFunctionInvocation(MethodInvocation node, |
| 242 AccessSemantics semantics) { | 256 AccessSemantics semantics) { |
| 243 // Locals don't need to be tree shaken. | 257 // Locals don't need to be tree shaken. |
| 244 } | 258 } |
| 245 | 259 |
| 246 @override | 260 @override |
| 247 void visitLocalVariableInvocation(MethodInvocation node, | 261 void visitLocalVariableInvocation(MethodInvocation node, |
| 248 AccessSemantics semantics) { | 262 AccessSemantics semantics) { |
| 249 // Locals don't need to be tree shaken. | 263 // Locals don't need to be tree shaken. |
| 250 } | 264 } |
| 251 | 265 |
| 252 @override | 266 @override |
| 253 void visitParameterInvocation(MethodInvocation node, | 267 void visitParameterInvocation(MethodInvocation node, |
| 254 AccessSemantics semantics) { | 268 AccessSemantics semantics) { |
| 255 // Locals don't need to be tree shaken. | 269 // Locals don't need to be tree shaken. |
| 256 } | 270 } |
| 257 | 271 |
| 258 @override | 272 @override |
| 259 void visitStaticFieldInvocation(MethodInvocation node, | 273 void visitStaticFieldInvocation(MethodInvocation node, |
| 260 AccessSemantics semantics) { | 274 AccessSemantics semantics) { |
| 261 // Invocation of a static field. | 275 // Invocation of a static field. |
| 262 analysis.accesses.add(semantics.element); | 276 analysis.accesses.add(semantics.element); |
| 263 analysis.invokes.add( | 277 analysis.invokes.add(createSelectorFromMethodInvocation(node, 'call')); |
| 264 createSelectorFromMethodInvocation(node, 'call')); | |
| 265 } | 278 } |
| 266 | 279 |
| 267 void visitStaticMethodInvocation(MethodInvocation node, | 280 void visitStaticMethodInvocation(MethodInvocation node, |
| 268 AccessSemantics semantics) { | 281 AccessSemantics semantics) { |
| 269 analysis.calls.add(semantics.element); | 282 analysis.calls.add(semantics.element); |
| 270 } | 283 } |
| 271 | 284 |
| 272 void visitStaticPropertyInvocation(MethodInvocation node, | 285 void visitStaticPropertyInvocation(MethodInvocation node, |
| 273 AccessSemantics semantics) { | 286 AccessSemantics semantics) { |
| 274 // Invocation of a property. TODO(paulberry): handle this. | 287 // Invocation of a property. TODO(paulberry): handle this. |
| 275 super.visitStaticPropertyInvocation(node, semantics); | 288 super.visitStaticPropertyInvocation(node, semantics); |
| 276 } | 289 } |
| 277 | 290 |
| 278 void visitDynamicAccess(AstNode node, AccessSemantics semantics) { | 291 void visitDynamicAccess(AstNode node, AccessSemantics semantics) { |
| 279 if (semantics.isRead) { | 292 if (semantics.isRead) { |
| 280 analysis.invokes.add( | 293 analysis.invokes.add( |
| 281 new Selector.getter(semantics.identifier.name, null)); | 294 new Selector.getter(semantics.identifier.name, null)); |
| 282 } | 295 } |
| 283 if (semantics.isWrite) { | 296 if (semantics.isWrite) { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 304 | 317 |
| 305 void visitStaticMethodAccess(AstNode node, AccessSemantics semantics) { | 318 void visitStaticMethodAccess(AstNode node, AccessSemantics semantics) { |
| 306 // Method tear-off. TODO(paulberry): implement. | 319 // Method tear-off. TODO(paulberry): implement. |
| 307 super.visitStaticMethodAccess(node, semantics); | 320 super.visitStaticMethodAccess(node, semantics); |
| 308 } | 321 } |
| 309 | 322 |
| 310 void visitStaticPropertyAccess(AstNode node, AccessSemantics semantics) { | 323 void visitStaticPropertyAccess(AstNode node, AccessSemantics semantics) { |
| 311 // TODO(paulberry): implement. | 324 // TODO(paulberry): implement. |
| 312 super.visitStaticPropertyAccess(node, semantics); | 325 super.visitStaticPropertyAccess(node, semantics); |
| 313 } | 326 } |
| 327 | |
| 328 @override | |
| 329 void visitConstructorDeclaration(ConstructorDeclaration node) { | |
|
Johnni Winther
2014/10/03 08:03:06
Currently we only mark classes as instantiated but
Paul Berry
2014/10/03 15:50:45
Ok. Currently I am only keeping track of what's b
| |
| 330 // TODO(paulberry): handle parameter list. | |
| 331 node.initializers.accept(this); | |
| 332 node.body.accept(this); | |
| 333 if (node.factoryKeyword == null) { | |
| 334 // This is a generative constructor. Figure out if it is redirecting. | |
| 335 // If it isn't, then the constructor instantiates the class so we need to | |
| 336 // add the class to analysis.instantiates. (If it is redirecting, then | |
| 337 // we don't need to, because the redirected-to constructor will take care | |
| 338 // of that). | |
| 339 if (node.initializers.length != 1 || node.initializers[0] is! RedirectingC onstructorInvocation) { | |
| 340 analysis.instantiates.add(node.element.enclosingElement); | |
| 341 } | |
| 342 } else if (node.redirectedConstructor != null) { | |
| 343 if (node.redirectedConstructor.staticElement == null) { | |
| 344 // Factory constructor redirects to a non-existent constructor. | |
| 345 // TODO(paulberry): handle this. | |
| 346 throw new UnimplementedError(); | |
| 347 } else { | |
| 348 analysis.calls.add(node.redirectedConstructor.staticElement); | |
| 349 } | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 @override | |
| 354 void | |
| 355 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation nod e) { | |
| 356 // Note: we don't have to worry about node.staticElement being | |
| 357 // null, because that would have been detected by the analyzer and | |
| 358 // reported as a compile time error. | |
| 359 analysis.calls.add(node.staticElement); | |
| 360 } | |
| 314 } | 361 } |
| OLD | NEW |