Index: pkg/analyzer2dart/lib/src/tree_shaker.dart |
diff --git a/pkg/analyzer2dart/lib/src/tree_shaker.dart b/pkg/analyzer2dart/lib/src/tree_shaker.dart |
index 9cd84231f27a5749f3d8532a60eca0ae7009ca97..7389853509bf52c101dcbdb473da849ffbe4fe29 100644 |
--- a/pkg/analyzer2dart/lib/src/tree_shaker.dart |
+++ b/pkg/analyzer2dart/lib/src/tree_shaker.dart |
@@ -11,6 +11,7 @@ import 'package:analyzer/src/generated/element.dart'; |
import 'package:compiler/implementation/universe/universe.dart'; |
import 'closed_world.dart'; |
+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
|
/** |
* The result of performing local reachability analysis on a method. |
@@ -24,7 +25,7 @@ class MethodAnalysis { |
/** |
* The functions statically called by the method. |
*/ |
- final List<LocalElement> calls = <LocalElement>[]; |
+ final List<ExecutableElement> calls = <ExecutableElement>[]; |
/** |
* The selectors used by the method to perform dynamic invocation. |
@@ -209,19 +210,6 @@ class TreeShakingVisitor extends RecursiveAstVisitor { |
TreeShakingVisitor(this.analysis); |
- /** |
- * Handle a true method call (a MethodInvocation that represents a call to |
- * a non-static method). |
- */ |
- void handleMethodCall(MethodInvocation node) { |
- analysis.invokes.add(createSelectorFromMethodInvocation(node)); |
- } |
- |
- @override |
- void visitFunctionDeclaration(FunctionDeclaration node) { |
- super.visitFunctionDeclaration(node); |
- } |
- |
@override |
void visitInstanceCreationExpression(InstanceCreationExpression node) { |
ConstructorElement staticElement = node.staticElement; |
@@ -240,110 +228,86 @@ class TreeShakingVisitor extends RecursiveAstVisitor { |
@override |
void visitMethodInvocation(MethodInvocation node) { |
- super.visitMethodInvocation(node); |
- Element staticElement = node.methodName.staticElement; |
- if (staticElement == null) { |
- if (node.realTarget != null) { |
- // Calling a method that has no known element, e.g.: |
- // dynamic x; |
- // x.foo(); |
- handleMethodCall(node); |
- } else { |
- // Calling a toplevel function which has no known element, e.g. |
- // main() { |
- // foo(); |
- // } |
- // TODO(paulberry): deal with this case. May need to notify the back |
- // end in case this makes it want to drag in some helper code. |
+ if (node.target != null) { |
+ node.target.accept(this); |
+ } |
+ node.argumentList.accept(this); |
+ AccessSemantics semantics = classifyMethodInvocation(node); |
+ switch (semantics.kind) { |
+ case AccessKind.DYNAMIC: |
+ analysis.invokes.add(createSelectorFromMethodInvocation(node)); |
+ break; |
+ case AccessKind.LOCAL_FUNCTION: |
+ case AccessKind.LOCAL_VARIABLE: |
+ case AccessKind.PARAMETER: |
+ // Locals don't need to be tree shaken. |
+ break; |
+ case AccessKind.STATIC_FIELD: |
+ // Invocation of a field. TODO(paulberry): handle this. |
+ throw new UnimplementedError(); |
+ case AccessKind.STATIC_METHOD: |
+ analysis.calls.add(semantics.element); |
+ break; |
+ case AccessKind.STATIC_PROPERTY: |
+ // Invocation of a property. TODO(paulberry): handle this. |
+ throw new UnimplementedError(); |
+ default: |
+ // Unexpected access kind. |
throw new UnimplementedError(); |
- } |
- } else if (staticElement is MethodElement) { |
- // Invoking a method, e.g.: |
- // class A { |
- // f() {} |
- // } |
- // main() { |
- // new A().f(); |
- // } |
- // or via implicit this, i.e.: |
- // class A { |
- // f() {} |
- // foo() { |
- // f(); |
- // } |
- // } |
- // TODO(paulberry): if user-provided types are wrong, this may actually |
- // be the PropertyAccessorElement case. |
- // TODO(paulberry): do we need to do something different for static |
- // methods? |
- handleMethodCall(node); |
- } else if (staticElement is PropertyAccessorElement) { |
- // Invoking a callable getter, e.g.: |
- // typedef FunctionType(); |
- // class A { |
- // FunctionType get f { ... } |
- // } |
- // main() { |
- // new A().f(); |
- // } |
- // or via implicit this, i.e.: |
- // typedef FunctionType(); |
- // class A { |
- // FunctionType get f { ... } |
- // foo() { |
- // f(); |
- // } |
- // } |
- // This also covers the case where the getter is synthetic, because we |
- // are getting a field (TODO(paulberry): verify that this is the case). |
- // TODO(paulberry): deal with this case. |
- // TODO(paulberry): if user-provided types are wrong, this may actually |
- // be the MethodElement case. |
- throw new UnimplementedError(); |
- } else if (staticElement is MultiplyInheritedExecutableElement) { |
- // TODO(paulberry): deal with this case. |
- throw new UnimplementedError(); |
- } else if (staticElement is LocalElement) { |
- // Invoking a callable local, e.g.: |
- // typedef FunctionType(); |
- // main() { |
- // FunctionType f = ...; |
- // f(); |
- // } |
- // or: |
- // main() { |
- // f() { ... } |
- // f(); |
- // } |
- // or: |
- // f() {} |
- // main() { |
- // f(); |
- // } |
- // TODO(paulberry): for the moment we are assuming it's a toplevel |
- // function. |
- analysis.calls.add(staticElement); |
- } else if (staticElement is MultiplyDefinedElement) { |
- // TODO(paulberry): do we have to deal with this case? |
- throw new UnimplementedError(); |
} |
- // TODO(paulberry): I believe all the other possibilities are errors, but |
- // we should double check. |
} |
@override |
void visitPropertyAccess(PropertyAccess node) { |
- // Accessing a getter or setter, e.g.: |
- // class A { |
- // get g() => ...; |
- // } |
- // main() { |
- // new A().g; |
- // } |
- // TODO(paulberry): do setters go through this path as well? |
- // TODO(paulberry): handle cases where the property access is represented |
- // as a PrefixedIdentifier. |
- super.visitPropertyAccess(node); |
- analysis.invokes.add(new Selector.getter(node.propertyName.name, null)); |
+ if (node.target != null) { |
+ node.target.accept(this); |
+ } |
+ _handlePropertyAccess(classifyPropertyAccess(node)); |
+ } |
+ |
+ @override |
+ visitPrefixedIdentifier(PrefixedIdentifier node) { |
+ node.prefix.accept(this); |
+ _handlePropertyAccess(classifyPrefixedIdentifier(node)); |
+ } |
+ |
+ @override |
+ visitSimpleIdentifier(SimpleIdentifier node) { |
+ AccessSemantics semantics = classifyBareIdentifier(node); |
+ if (semantics != null) { |
+ _handlePropertyAccess(semantics); |
+ } |
+ } |
+ |
+ void _handlePropertyAccess(AccessSemantics semantics) { |
+ switch (semantics.kind) { |
+ case AccessKind.DYNAMIC: |
+ if (semantics.isRead) { |
+ analysis.invokes.add( |
+ new Selector.getter(semantics.identifier.name, null)); |
+ } |
+ if (semantics.isWrite) { |
+ // TODO(paulberry): implement. |
+ throw new UnimplementedError(); |
+ } |
+ break; |
+ case AccessKind.LOCAL_FUNCTION: |
+ case AccessKind.LOCAL_VARIABLE: |
+ case AccessKind.PARAMETER: |
+ // Locals don't need to be tree shaken. |
+ break; |
+ case AccessKind.STATIC_FIELD: |
+ // TODO(paulberry): implement. |
+ throw new UnimplementedError(); |
+ case AccessKind.STATIC_METHOD: |
+ // Method tear-off. TODO(paulberry): implement. |
+ break; |
+ case AccessKind.STATIC_PROPERTY: |
+ // TODO(paulberry): implement. |
+ throw new UnimplementedError(); |
+ default: |
+ // Unexpected access kind. |
+ throw new UnimplementedError(); |
+ } |
} |
} |