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'; |
| 11 import 'package:compiler/implementation/universe/universe.dart'; | 11 import 'package:compiler/implementation/universe/universe.dart'; |
| 12 | 12 |
| 13 import 'closed_world.dart'; | 13 import 'closed_world.dart'; |
| 14 import 'package:analyzer2dart/src/identifier_semantics.dart'; | |
|
Johnni Winther
2014/09/25 12:09:36
What is the motivation for using a package and not
Paul Berry
2014/09/25 18:22:48
Hmm, I hadn't actually given it much thought. Thi
| |
| 14 | 15 |
| 15 /** | 16 /** |
| 16 * The result of performing local reachability analysis on a method. | 17 * The result of performing local reachability analysis on a method. |
| 17 */ | 18 */ |
| 18 class MethodAnalysis { | 19 class MethodAnalysis { |
| 19 /** | 20 /** |
| 20 * The AST for the method. | 21 * The AST for the method. |
| 21 */ | 22 */ |
| 22 final Declaration declaration; | 23 final Declaration declaration; |
| 23 | 24 |
| 24 /** | 25 /** |
| 25 * The functions statically called by the method. | 26 * The functions statically called by the method. |
| 26 */ | 27 */ |
| 27 final List<LocalElement> calls = <LocalElement>[]; | 28 final List<ExecutableElement> calls = <ExecutableElement>[]; |
| 28 | 29 |
| 29 /** | 30 /** |
| 30 * The selectors used by the method to perform dynamic invocation. | 31 * The selectors used by the method to perform dynamic invocation. |
| 31 */ | 32 */ |
| 32 final List<Selector> invokes = <Selector>[]; | 33 final List<Selector> invokes = <Selector>[]; |
| 33 | 34 |
| 34 /** | 35 /** |
| 35 * The classes that are instantiated by the method. | 36 * The classes that are instantiated by the method. |
| 36 */ | 37 */ |
| 37 final List<ClassElement> instantiates = <ClassElement>[]; | 38 final List<ClassElement> instantiates = <ClassElement>[]; |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 } | 203 } |
| 203 } | 204 } |
| 204 return new Selector.call(node.methodName.name, null, arity, namedArguments); | 205 return new Selector.call(node.methodName.name, null, arity, namedArguments); |
| 205 } | 206 } |
| 206 | 207 |
| 207 class TreeShakingVisitor extends RecursiveAstVisitor { | 208 class TreeShakingVisitor extends RecursiveAstVisitor { |
| 208 final MethodAnalysis analysis; | 209 final MethodAnalysis analysis; |
| 209 | 210 |
| 210 TreeShakingVisitor(this.analysis); | 211 TreeShakingVisitor(this.analysis); |
| 211 | 212 |
| 212 /** | |
| 213 * Handle a true method call (a MethodInvocation that represents a call to | |
| 214 * a non-static method). | |
| 215 */ | |
| 216 void handleMethodCall(MethodInvocation node) { | |
| 217 analysis.invokes.add(createSelectorFromMethodInvocation(node)); | |
| 218 } | |
| 219 | |
| 220 @override | |
| 221 void visitFunctionDeclaration(FunctionDeclaration node) { | |
| 222 super.visitFunctionDeclaration(node); | |
| 223 } | |
| 224 | |
| 225 @override | 213 @override |
| 226 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 214 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 227 ConstructorElement staticElement = node.staticElement; | 215 ConstructorElement staticElement = node.staticElement; |
| 228 if (staticElement != null) { | 216 if (staticElement != null) { |
| 229 // TODO(paulberry): Really we should enqueue the constructor, and then | 217 // TODO(paulberry): Really we should enqueue the constructor, and then |
| 230 // when we visit it add the class to the class bucket. | 218 // when we visit it add the class to the class bucket. |
| 231 ClassElement classElement = staticElement.enclosingElement; | 219 ClassElement classElement = staticElement.enclosingElement; |
| 232 analysis.instantiates.add(classElement); | 220 analysis.instantiates.add(classElement); |
| 233 } else { | 221 } else { |
| 234 // TODO(paulberry): deal with this situation. This can happen, for | 222 // TODO(paulberry): deal with this situation. This can happen, for |
| 235 // example, in the case "main() => new Unresolved();" (which is a | 223 // example, in the case "main() => new Unresolved();" (which is a |
| 236 // warning, not an error). | 224 // warning, not an error). |
| 237 } | 225 } |
| 238 super.visitInstanceCreationExpression(node); | 226 super.visitInstanceCreationExpression(node); |
| 239 } | 227 } |
| 240 | 228 |
| 241 @override | 229 @override |
| 242 void visitMethodInvocation(MethodInvocation node) { | 230 void visitMethodInvocation(MethodInvocation node) { |
| 243 super.visitMethodInvocation(node); | 231 if (node.target != null) { |
| 244 Element staticElement = node.methodName.staticElement; | 232 node.target.accept(this); |
| 245 if (staticElement == null) { | 233 } |
| 246 if (node.realTarget != null) { | 234 node.argumentList.accept(this); |
| 247 // Calling a method that has no known element, e.g.: | 235 AccessSemantics semantics = classifyMethodInvocation(node); |
| 248 // dynamic x; | 236 switch (semantics.kind) { |
| 249 // x.foo(); | 237 case AccessKind.DYNAMIC: |
| 250 handleMethodCall(node); | 238 analysis.invokes.add(createSelectorFromMethodInvocation(node)); |
| 251 } else { | 239 break; |
| 252 // Calling a toplevel function which has no known element, e.g. | 240 case AccessKind.LOCAL_FUNCTION: |
| 253 // main() { | 241 case AccessKind.LOCAL_VARIABLE: |
| 254 // foo(); | 242 case AccessKind.PARAMETER: |
| 255 // } | 243 // Locals don't need to be tree shaken. |
| 256 // TODO(paulberry): deal with this case. May need to notify the back | 244 break; |
| 257 // end in case this makes it want to drag in some helper code. | 245 case AccessKind.STATIC_FIELD: |
| 246 // Invocation of a field. TODO(paulberry): handle this. | |
| 258 throw new UnimplementedError(); | 247 throw new UnimplementedError(); |
| 259 } | 248 case AccessKind.STATIC_METHOD: |
| 260 } else if (staticElement is MethodElement) { | 249 analysis.calls.add(semantics.element); |
| 261 // Invoking a method, e.g.: | 250 break; |
| 262 // class A { | 251 case AccessKind.STATIC_PROPERTY: |
| 263 // f() {} | 252 // Invocation of a property. TODO(paulberry): handle this. |
| 264 // } | 253 throw new UnimplementedError(); |
| 265 // main() { | 254 default: |
| 266 // new A().f(); | 255 // Unexpected access kind. |
| 267 // } | 256 throw new UnimplementedError(); |
| 268 // or via implicit this, i.e.: | |
| 269 // class A { | |
| 270 // f() {} | |
| 271 // foo() { | |
| 272 // f(); | |
| 273 // } | |
| 274 // } | |
| 275 // TODO(paulberry): if user-provided types are wrong, this may actually | |
| 276 // be the PropertyAccessorElement case. | |
| 277 // TODO(paulberry): do we need to do something different for static | |
| 278 // methods? | |
| 279 handleMethodCall(node); | |
| 280 } else if (staticElement is PropertyAccessorElement) { | |
| 281 // Invoking a callable getter, e.g.: | |
| 282 // typedef FunctionType(); | |
| 283 // class A { | |
| 284 // FunctionType get f { ... } | |
| 285 // } | |
| 286 // main() { | |
| 287 // new A().f(); | |
| 288 // } | |
| 289 // or via implicit this, i.e.: | |
| 290 // typedef FunctionType(); | |
| 291 // class A { | |
| 292 // FunctionType get f { ... } | |
| 293 // foo() { | |
| 294 // f(); | |
| 295 // } | |
| 296 // } | |
| 297 // This also covers the case where the getter is synthetic, because we | |
| 298 // are getting a field (TODO(paulberry): verify that this is the case). | |
| 299 // TODO(paulberry): deal with this case. | |
| 300 // TODO(paulberry): if user-provided types are wrong, this may actually | |
| 301 // be the MethodElement case. | |
| 302 throw new UnimplementedError(); | |
| 303 } else if (staticElement is MultiplyInheritedExecutableElement) { | |
| 304 // TODO(paulberry): deal with this case. | |
| 305 throw new UnimplementedError(); | |
| 306 } else if (staticElement is LocalElement) { | |
| 307 // Invoking a callable local, e.g.: | |
| 308 // typedef FunctionType(); | |
| 309 // main() { | |
| 310 // FunctionType f = ...; | |
| 311 // f(); | |
| 312 // } | |
| 313 // or: | |
| 314 // main() { | |
| 315 // f() { ... } | |
| 316 // f(); | |
| 317 // } | |
| 318 // or: | |
| 319 // f() {} | |
| 320 // main() { | |
| 321 // f(); | |
| 322 // } | |
| 323 // TODO(paulberry): for the moment we are assuming it's a toplevel | |
| 324 // function. | |
| 325 analysis.calls.add(staticElement); | |
| 326 } else if (staticElement is MultiplyDefinedElement) { | |
| 327 // TODO(paulberry): do we have to deal with this case? | |
| 328 throw new UnimplementedError(); | |
| 329 } | 257 } |
| 330 // TODO(paulberry): I believe all the other possibilities are errors, but | |
| 331 // we should double check. | |
| 332 } | 258 } |
| 333 | 259 |
| 334 @override | 260 @override |
| 335 void visitPropertyAccess(PropertyAccess node) { | 261 void visitPropertyAccess(PropertyAccess node) { |
| 336 // Accessing a getter or setter, e.g.: | 262 if (node.target != null) { |
| 337 // class A { | 263 node.target.accept(this); |
| 338 // get g() => ...; | 264 } |
| 339 // } | 265 _handlePropertyAccess(classifyPropertyAccess(node)); |
| 340 // main() { | 266 } |
| 341 // new A().g; | 267 |
| 342 // } | 268 @override |
| 343 // TODO(paulberry): do setters go through this path as well? | 269 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 344 // TODO(paulberry): handle cases where the property access is represented | 270 node.prefix.accept(this); |
| 345 // as a PrefixedIdentifier. | 271 _handlePropertyAccess(classifyPrefixedIdentifier(node)); |
| 346 super.visitPropertyAccess(node); | 272 } |
| 347 analysis.invokes.add(new Selector.getter(node.propertyName.name, null)); | 273 |
| 274 @override | |
| 275 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 276 AccessSemantics semantics = classifyBareIdentifier(node); | |
| 277 if (semantics != null) { | |
| 278 _handlePropertyAccess(semantics); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 void _handlePropertyAccess(AccessSemantics semantics) { | |
| 283 switch (semantics.kind) { | |
| 284 case AccessKind.DYNAMIC: | |
| 285 if (semantics.isRead) { | |
| 286 analysis.invokes.add( | |
| 287 new Selector.getter(semantics.identifier.name, null)); | |
| 288 } | |
| 289 if (semantics.isWrite) { | |
| 290 // TODO(paulberry): implement. | |
| 291 throw new UnimplementedError(); | |
| 292 } | |
| 293 break; | |
| 294 case AccessKind.LOCAL_FUNCTION: | |
| 295 case AccessKind.LOCAL_VARIABLE: | |
| 296 case AccessKind.PARAMETER: | |
| 297 // Locals don't need to be tree shaken. | |
| 298 break; | |
| 299 case AccessKind.STATIC_FIELD: | |
| 300 // TODO(paulberry): implement. | |
| 301 throw new UnimplementedError(); | |
| 302 case AccessKind.STATIC_METHOD: | |
| 303 // Method tear-off. TODO(paulberry): implement. | |
| 304 break; | |
| 305 case AccessKind.STATIC_PROPERTY: | |
| 306 // TODO(paulberry): implement. | |
| 307 throw new UnimplementedError(); | |
| 308 default: | |
| 309 // Unexpected access kind. | |
| 310 throw new UnimplementedError(); | |
| 311 } | |
| 348 } | 312 } |
| 349 } | 313 } |
| OLD | NEW |