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

Unified Diff: pkg/analyzer2dart/lib/src/identifier_semantics.dart

Issue 587323004: Classify method and property accesses semantically. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer2dart/lib/src/tree_shaker.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer2dart/lib/src/identifier_semantics.dart
diff --git a/pkg/analyzer2dart/lib/src/identifier_semantics.dart b/pkg/analyzer2dart/lib/src/identifier_semantics.dart
new file mode 100644
index 0000000000000000000000000000000000000000..95add6d52f1f87629bc83ca8cfa83e93e9d50ed0
--- /dev/null
+++ b/pkg/analyzer2dart/lib/src/identifier_semantics.dart
@@ -0,0 +1,325 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Code for classifying the semantics of identifiers appearing in a Dart file.
+ */
+library analyzer2dart.identifierSemantics;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/element.dart';
+
+/**
+ * Base class used to classify the semantics of a method or function
+ * invocation.
+ */
+abstract class MethodInvocationSemantics {}
+
+/**
+ * MethodInvocationSemantics object representing the invocation of a method
+ * that is defined statically within a class, or of a function that is defined
+ * at toplevel within a library.
+ */
+class StaticMethodInvocation extends MethodInvocationSemantics {
+ /**
+ * The method or function being invoked.
+ */
+ final ExecutableElement methodElement;
+
+ /**
+ * The class containing the member being invoked, or null if a toplevel
+ * function is being invoked.
+ */
+ final ClassElement classElement;
+
+ /**
+ * The identifier being used to invoke the method or function.
+ */
+ final SimpleIdentifier methodName;
+
+ // TODO(paulberry): would it also be useful to store the libraryElement?
+
+ StaticMethodInvocation(this.methodElement, this.classElement,
+ this.methodName);
+}
+
+/**
+ * MethodInvocationSemantics object representing the invocation of a function
+ * that is defined inside of another function or method.
+ */
+class LocalFunctionInvocation extends MethodInvocationSemantics {
+ /**
+ * The function being invoked.
+ */
+ final FunctionElement functionElement;
+
+ LocalFunctionInvocation(this.functionElement);
+}
+
+/**
+ * MethodInvocationSemantics object representing the invocation of an instance
+ * method within a class.
+ *
+ * This also handles calls to an undefined method using a bare
+ * identifier (since it cannot be determined until runtime whether the method
+ * is truly undefined).
+ */
+class DynamicMethodInvocation extends MethodInvocationSemantics {
+ /**
+ * The expression on which the method is being invoked (or null if the target
+ * is an implicit "this").
+ */
+ final Expression target;
+
+ /**
+ * The identifier being used to invoke the method.
+ */
+ final SimpleIdentifier methodName;
+
+ DynamicMethodInvocation(this.target, this.methodName);
+}
+
+/**
+ * Base class used to classify the semantics of a property access.
+ */
+abstract class AccessSemantics {
+ /**
+ * The identifier being used to access the property.
+ */
+ final SimpleIdentifier _identifier;
+
+ AccessSemantics(this._identifier);
+
+ /**
+ * True if this is a read access to the property. Note that both
+ * [isRead] and [isWrite] will be true in the case of a read-modify-write
+ * operation (e.g. "+=").
+ */
+ bool get isRead => _identifier.inGetterContext();
+
+ /**
+ * True if this is a write access to the property. Note that both
+ * [isRead] and [isWrite] will be true in the case of a read-modify-write
+ * operation (e.g. "+=").
+ */
+ bool get isWrite => _identifier.inSetterContext();
+}
+
+/**
+ * AccessSemantics object representing an access to a field that is defined
+ * statically within a class, or to a variable that is defined at toplevel
+ * within a library.
+ */
+class StaticFieldAccess extends AccessSemantics {
+ /**
+ * The field or variable being referenced.
+ */
+ final PropertyInducingElement field;
+
+ /**
+ * The class containing the field being referenced, or null if a toplevel
+ * variable is being referenced.
+ */
+ final ClassElement classElement;
+
+ // TODO(paulberry): would it also be useful to store the libraryElement?
+
+ StaticFieldAccess(this.field, this.classElement, SimpleIdentifier identifier)
+ : super(identifier);
+}
+
+/**
+ * AccessSemantics object representing an access to a property that is defined
+ * statically within a class, or defined at toplevel within a library.
+ */
+class StaticPropertyAccess extends AccessSemantics {
+ /**
+ * The property being referenced, or null if the property is undefined.
+ */
+ final PropertyAccessorElement property;
+
+ /**
+ * The class containing the property being referenced, or null if a toplevel
+ * property is being referenced.
+ */
+ final ClassElement classElement;
+
+ /**
+ * The identifier being used to access the property.
+ */
+ SimpleIdentifier get propertyName => _identifier;
+
+ // TODO(paulberry): would it also be useful to store the libraryElement?
+
+ StaticPropertyAccess(this.property, this.classElement,
+ SimpleIdentifier propertyName)
+ : super(propertyName);
+}
+
+/**
+ * AccessSemantics object representing an access to an instance property or
+ * field within a class.
+ *
+ * This also handles access to an undefined property using a bare
+ * identifier (since it cannot be determined until runtime whether the property
+ * is truly undefined).
+ */
+class DynamicPropertyAccess extends AccessSemantics {
+ /**
+ * The expression on which the method is being invoked (or null if the target
+ * is an implicit "this").
+ */
+ final Expression target;
+
+ /**
+ * The identifier being used to access the property.
+ */
+ final SimpleIdentifier propertyName;
+
+ DynamicPropertyAccess(this.target, SimpleIdentifier propertyName)
+ : super(propertyName),
+ propertyName = propertyName;
+}
+
+/**
+ * AccessSemantics object representing an access to a local variable within a
+ * function or method.
+ */
+class LocalVariableAccess extends AccessSemantics {
+ final LocalVariableElement variable;
+ LocalVariableAccess(this.variable, SimpleIdentifier identifier)
+ : super(identifier);
+}
+
+/**
+ * Return the method invocation semantics for [node].
+ */
+MethodInvocationSemantics classifyMethodInvocation(MethodInvocation node) {
+ Expression target = node.realTarget;
+ if (target == null) {
+ Element staticElement = node.methodName.staticElement;
+ if (staticElement is FunctionElement) {
+ if (staticElement.enclosingElement is CompilationUnitElement) {
+ return new StaticMethodInvocation(staticElement, null, node.methodName);
+ } else {
+ return new LocalFunctionInvocation(staticElement);
+ }
+ } else if (staticElement is MethodElement && staticElement.isStatic) {
+ return new StaticMethodInvocation(
+ staticElement,
+ staticElement.enclosingElement,
+ node.methodName);
+ }
Brian Wilkerson 2014/09/22 19:44:48 It's also possible for the static element to be a
Paul Berry 2014/09/23 16:32:49 Done.
+ } else if (target is SimpleIdentifier) {
+ Element targetStaticElement = target.staticElement;
+ if (targetStaticElement is PrefixElement) {
+ if (node.methodName.staticElement != null) {
+ return new StaticMethodInvocation(
+ node.methodName.staticElement,
+ null,
+ node.methodName);
+ } else {
+ return new DynamicMethodInvocation(null, node.methodName);
+ }
+ } else if (targetStaticElement is ClassElement) {
+ return new StaticMethodInvocation(
+ node.methodName.staticElement,
+ targetStaticElement,
+ node.methodName);
+ }
+ }
Brian Wilkerson 2014/09/22 19:44:47 This is missing the case where the target is a Pre
Paul Berry 2014/09/23 16:32:49 Done. It turned out that I was able to handle thi
+ return new DynamicMethodInvocation(target, node.methodName);
+}
+
+/**
+ * Return the access semantics for [node].
+ */
+AccessSemantics classifyPrefixedIdentifier(PrefixedIdentifier node) {
+ Element prefixElement = node.prefix.staticElement;
+ Element suffixElement = node.identifier.staticElement;
+ if (prefixElement is PrefixElement) {
+ Element staticElement = node.identifier.staticElement;
Brian Wilkerson 2014/09/22 19:44:48 staticElement == suffixElement
Paul Berry 2014/09/23 16:32:49 Done.
+ if (staticElement is PropertyAccessorElement) {
+ if (staticElement.isSynthetic) {
+ return new StaticFieldAccess(
+ staticElement.variable,
+ null,
+ node.identifier);
+ } else {
+ return new StaticPropertyAccess(staticElement, null, node.identifier);
+ }
+ }
+ return new DynamicPropertyAccess(null, node.identifier);
+ } else if (prefixElement is ClassElement) {
+ if (suffixElement is PropertyAccessorElement && suffixElement.isSynthetic) {
+ return new StaticFieldAccess(
+ suffixElement.variable,
+ prefixElement,
+ node.identifier);
+ }
+ return new StaticPropertyAccess(
+ suffixElement,
+ prefixElement,
+ node.identifier);
+ } else {
+ return new DynamicPropertyAccess(node.prefix, node.identifier);
Brian Wilkerson 2014/09/22 19:44:47 Do we represent method tear-offs as property acces
Paul Berry 2014/09/23 16:32:49 Method tear-offs weren't being handled properly.
+ }
+}
+
+/**
+ * Return the access semantics for [node].
+ */
+DynamicPropertyAccess classifyPropertyAccess(PropertyAccess node) {
+ return new DynamicPropertyAccess(node.realTarget, node.propertyName);
+}
+
+/**
+ * Return the access semantics for [node]. Caller must ensure that [node] is
+ * not the right hand side of a [PropertyAccess] or [PrefixedIdentifier], or
+ * the method name of a [MethodInvocation].
+ */
+AccessSemantics classifyBareIdentifier(SimpleIdentifier node) {
+ AstNode parent = node.parent;
+ if ((parent is FunctionDeclaration && parent.name == node) ||
Brian Wilkerson 2014/09/22 19:44:47 Replace this 'if' with if (node.inDeclarationCont
Paul Berry 2014/09/23 16:32:49 Aha, that's much better. Thanks!
+ parent is ImportDirective ||
+ (parent is ClassDeclaration && parent.name == node) ||
+ (parent is MethodDeclaration && parent.name == node) ||
+ (parent is VariableDeclaration && parent.name == node) ||
+ (parent is FormalParameter && parent.identifier == node)) {
+ // This identifier is a declaration, not a use.
+ // TODO(paulberry): Can some of the above cases be coalesced?
+ // TODO(paulberry): Additional cases
+ return null;
+ }
+ if (parent is TypeName) {
+ // TODO(paulberry): handle this case.
Brian Wilkerson 2014/09/22 19:44:48 It might be cleanest to not visit the children of
Paul Berry 2014/09/23 16:32:49 Hmm, that may be true. I'm not sure whether I wan
Brian Wilkerson 2014/09/23 17:03:58 It looks like you're currently exiting if you're v
Paul Berry 2014/09/23 17:45:56 Acknowledged.
+ return null;
+ }
+ // TODO(paulberry): handle PrefixElement.
+ Element staticElement = node.staticElement;
+ if (staticElement is PropertyAccessorElement) {
+ if (staticElement.isSynthetic) {
+ if (staticElement.enclosingElement is CompilationUnitElement) {
+ return new StaticFieldAccess(staticElement.variable, null, node);
+ } else if (staticElement.isStatic) {
+ return new StaticFieldAccess(
+ staticElement.variable,
+ staticElement.enclosingElement,
+ node);
+ }
+ } else {
+ if (staticElement.enclosingElement is CompilationUnitElement) {
+ return new StaticPropertyAccess(staticElement, null, node);
+ } else if (staticElement.isStatic) {
+ return new StaticPropertyAccess(
+ staticElement,
+ staticElement.enclosingElement,
+ node);
+ }
+ }
+ } else if (staticElement is LocalVariableElement) {
+ return new LocalVariableAccess(staticElement, node);
+ }
Brian Wilkerson 2014/09/22 19:44:48 Missing ParameterElement?
Paul Berry 2014/09/23 16:32:49 Done.
+ return new DynamicPropertyAccess(null, node);
+}
« no previous file with comments | « no previous file | pkg/analyzer2dart/lib/src/tree_shaker.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698