| 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'; |
| 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 MethodInvocationSemantics semantics = classifyMethodInvocation(node); |
| 248 // dynamic x; | 236 if (semantics is StaticMethodInvocation) { |
| 249 // x.foo(); | 237 analysis.calls.add(semantics.methodElement); |
| 250 handleMethodCall(node); | 238 } else if (semantics is LocalFunctionInvocation) { |
| 251 } else { | 239 // Tree shaking doesn't need to deal with local functions. |
| 252 // Calling a toplevel function which has no known element, e.g. | 240 } else if (semantics is DynamicMethodInvocation) { |
| 253 // main() { | 241 analysis.invokes.add(createSelectorFromMethodInvocation(node)); |
| 254 // foo(); | 242 } else { |
| 255 // } | 243 // Unexpected method invocation semantics. |
| 256 // TODO(paulberry): deal with this case. May need to notify the back | |
| 257 // end in case this makes it want to drag in some helper code. | |
| 258 throw new UnimplementedError(); | |
| 259 } | |
| 260 } else if (staticElement is MethodElement) { | |
| 261 // Invoking a method, e.g.: | |
| 262 // class A { | |
| 263 // f() {} | |
| 264 // } | |
| 265 // main() { | |
| 266 // new A().f(); | |
| 267 // } | |
| 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(); | 244 throw new UnimplementedError(); |
| 329 } | 245 } |
| 330 // TODO(paulberry): I believe all the other possibilities are errors, but | |
| 331 // we should double check. | |
| 332 } | 246 } |
| 333 | 247 |
| 334 @override | 248 @override |
| 335 void visitPropertyAccess(PropertyAccess node) { | 249 void visitPropertyAccess(PropertyAccess node) { |
| 336 // Accessing a getter or setter, e.g.: | 250 if (node.target != null) { |
| 337 // class A { | 251 node.target.accept(this); |
| 338 // get g() => ...; | 252 } |
| 339 // } | 253 _handlePropertyAccess(classifyPropertyAccess(node)); |
| 340 // main() { | 254 } |
| 341 // new A().g; | 255 |
| 342 // } | 256 @override |
| 343 // TODO(paulberry): do setters go through this path as well? | 257 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 344 // TODO(paulberry): handle cases where the property access is represented | 258 node.prefix.accept(this); |
| 345 // as a PrefixedIdentifier. | 259 _handlePropertyAccess(classifyPrefixedIdentifier(node)); |
| 346 super.visitPropertyAccess(node); | 260 } |
| 347 analysis.invokes.add(new Selector.getter(node.propertyName.name, null)); | 261 |
| 262 @override |
| 263 visitSimpleIdentifier(SimpleIdentifier node) { |
| 264 AccessSemantics semantics = classifyBareIdentifier(node); |
| 265 if (semantics != null) { |
| 266 _handlePropertyAccess(semantics); |
| 267 } |
| 268 } |
| 269 |
| 270 void _handlePropertyAccess(AccessSemantics semantics) { |
| 271 if (semantics is StaticFieldAccess) { |
| 272 // TODO(paulberry): implement. |
| 273 throw new UnimplementedError(); |
| 274 } else if (semantics is StaticPropertyAccess) { |
| 275 // TODO(paulberry): implement. |
| 276 throw new UnimplementedError(); |
| 277 } else if (semantics is DynamicPropertyAccess) { |
| 278 if (semantics.isRead) { |
| 279 analysis.invokes.add( |
| 280 new Selector.getter(semantics.propertyName.name, null)); |
| 281 } |
| 282 if (semantics.isWrite) { |
| 283 // TODO(paulberry): implement. |
| 284 throw new UnimplementedError(); |
| 285 } |
| 286 } else if (semantics is LocalVariableAccess) { |
| 287 // Tree shaking doesn't need to deal with local variables. |
| 288 } else { |
| 289 // Unexpected property access semantics. |
| 290 } |
| 348 } | 291 } |
| 349 } | 292 } |
| OLD | NEW |