aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java')
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java563
1 files changed, 451 insertions, 112 deletions
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
index 54828c7d31..85ad7b4b00 100644
--- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
@@ -1,6 +1,25 @@
package org.checkerframework.dataflow.analysis;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
import org.checkerframework.dataflow.cfg.node.ArrayAccessNode;
+import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
import org.checkerframework.dataflow.cfg.node.ClassNameNode;
import org.checkerframework.dataflow.cfg.node.ExplicitThisLiteralNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
@@ -15,41 +34,33 @@ import org.checkerframework.dataflow.cfg.node.ValueLiteralNode;
import org.checkerframework.dataflow.cfg.node.WideningConversionNode;
import org.checkerframework.dataflow.util.HashCodeUtils;
import org.checkerframework.dataflow.util.PurityUtils;
-
import org.checkerframework.javacutil.AnnotationProvider;
import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.ErrorReporter;
+import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.TreeUtils;
+import org.checkerframework.javacutil.TypeAnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
/**
- * Collection of classes and helper functions to represent Java expressions
- * about which the org.checkerframework.dataflow analysis can possibly infer facts. Expressions
- * include:
+ * Collection of classes and helper functions to represent Java expressions about which the
+ * org.checkerframework.dataflow analysis can possibly infer facts. Expressions include:
+ *
* <ul>
- * <li>Field accesses (e.g., <em>o.f</em>)</li>
- * <li>Local variables (e.g., <em>l</em>)</li>
- * <li>This reference (e.g., <em>this</em>)</li>
- * <li>Pure method calls (e.g., <em>o.m()</em>)</li>
- * <li>Unknown other expressions to mark that something else was present.</li>
+ * <li>Field accesses (e.g., <em>o.f</em>)
+ * <li>Local variables (e.g., <em>l</em>)
+ * <li>This reference (e.g., <em>this</em>)
+ * <li>Pure method calls (e.g., <em>o.m()</em>)
+ * <li>Unknown other expressions to mark that something else was present.
* </ul>
*
* @author Stefan Heule
- *
*/
public class FlowExpressions {
/**
- * @return The internal representation (as {@link FieldAccess}) of a
- * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver.
+ * @return the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}.
+ * Can contain {@link Unknown} as receiver.
*/
public static FieldAccess internalReprOfFieldAccess(
AnnotationProvider provider, FieldAccessNode node) {
@@ -64,8 +75,8 @@ public class FlowExpressions {
}
/**
- * @return The internal representation (as {@link FieldAccess}) of a
- * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver.
+ * @return the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}.
+ * Can contain {@link Unknown} as receiver.
*/
public static ArrayAccess internalReprOfArrayAccess(
AnnotationProvider provider, ArrayAccessNode node) {
@@ -75,26 +86,25 @@ public class FlowExpressions {
}
/**
- * We ignore operations such as widening and
- * narrowing when computing the internal representation.
+ * We ignore operations such as widening and narrowing when computing the internal
+ * representation.
*
- * @return The internal representation (as {@link Receiver}) of any
- * {@link Node}. Might contain {@link Unknown}.
+ * @return the internal representation (as {@link Receiver}) of any {@link Node}. Might contain
+ * {@link Unknown}.
*/
- public static Receiver internalReprOf(AnnotationProvider provider,
- Node receiverNode) {
+ public static Receiver internalReprOf(AnnotationProvider provider, Node receiverNode) {
return internalReprOf(provider, receiverNode, false);
}
/**
- * We ignore operations such as widening and
- * narrowing when computing the internal representation.
+ * We ignore operations such as widening and narrowing when computing the internal
+ * representation.
*
- * @return The internal representation (as {@link Receiver}) of any
- * {@link Node}. Might contain {@link Unknown}.
+ * @return the internal representation (as {@link Receiver}) of any {@link Node}. Might contain
+ * {@link Unknown}.
*/
- public static Receiver internalReprOf(AnnotationProvider provider,
- Node receiverNode, boolean allowNonDeterminitic) {
+ public static Receiver internalReprOf(
+ AnnotationProvider provider, Node receiverNode, boolean allowNonDeterministic) {
Receiver receiver = null;
if (receiverNode instanceof FieldAccessNode) {
FieldAccessNode fan = (FieldAccessNode) receiverNode;
@@ -103,6 +113,12 @@ public class FlowExpressions {
// For some reason, "className.this" is considered a field access.
// We right this wrong here.
receiver = new ThisReference(fan.getReceiver().getType());
+ } else if (fan.getFieldName().equals("class")) {
+ // "className.class" is considered a field access. This makes sense,
+ // since .class is similar to a field access which is the equivalent
+ // of a call to getClass(). However for the purposes of dataflow
+ // analysis, and value stores, this is the equivalent of a ClassNameNode.
+ receiver = new ClassName(fan.getReceiver().getType());
} else {
receiver = internalReprOfFieldAccess(provider, fan);
}
@@ -120,26 +136,25 @@ public class FlowExpressions {
receiver = internalReprOfArrayAccess(provider, a);
} else if (receiverNode instanceof StringConversionNode) {
// ignore string conversion
- return internalReprOf(provider,
- ((StringConversionNode) receiverNode).getOperand());
+ return internalReprOf(provider, ((StringConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof WideningConversionNode) {
// ignore widening
- return internalReprOf(provider,
- ((WideningConversionNode) receiverNode).getOperand());
+ return internalReprOf(provider, ((WideningConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof NarrowingConversionNode) {
// ignore narrowing
- return internalReprOf(provider,
- ((NarrowingConversionNode) receiverNode).getOperand());
+ return internalReprOf(provider, ((NarrowingConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof ClassNameNode) {
ClassNameNode cn = (ClassNameNode) receiverNode;
receiver = new ClassName(cn.getType());
} else if (receiverNode instanceof ValueLiteralNode) {
ValueLiteralNode vn = (ValueLiteralNode) receiverNode;
receiver = new ValueLiteral(vn.getType(), vn);
+ } else if (receiverNode instanceof ArrayCreationNode) {
+ ArrayCreationNode an = (ArrayCreationNode) receiverNode;
+ receiver = new ArrayCreation(an.getType(), an.getDimensions(), an.getInitializers());
} else if (receiverNode instanceof MethodInvocationNode) {
MethodInvocationNode mn = (MethodInvocationNode) receiverNode;
- ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn
- .getTree());
+ ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn.getTree());
// check if this represents a boxing operation of a constant, in which
// case we treat the method call as deterministic, because there is no way
@@ -153,21 +168,20 @@ public class FlowExpressions {
}
}
- if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterminitic || considerDeterministic) {
+ if (PurityUtils.isDeterministic(provider, invokedMethod)
+ || allowNonDeterministic
+ || considerDeterministic) {
List<Receiver> parameters = new ArrayList<>();
for (Node p : mn.getArguments()) {
parameters.add(internalReprOf(provider, p));
}
Receiver methodReceiver;
if (ElementUtils.isStatic(invokedMethod)) {
- methodReceiver = new ClassName(mn.getTarget().getReceiver()
- .getType());
+ methodReceiver = new ClassName(mn.getTarget().getReceiver().getType());
} else {
- methodReceiver = internalReprOf(provider, mn.getTarget()
- .getReceiver());
+ methodReceiver = internalReprOf(provider, mn.getTarget().getReceiver());
}
- receiver = new PureMethodCall(mn.getType(), invokedMethod,
- methodReceiver, parameters);
+ receiver = new MethodCall(mn.getType(), invokedMethod, methodReceiver, parameters);
}
}
@@ -177,7 +191,209 @@ public class FlowExpressions {
return receiver;
}
- public static abstract class Receiver {
+ /**
+ * @return the internal representation (as {@link Receiver}) of any {@link ExpressionTree}.
+ * Might contain {@link Unknown}.
+ */
+ public static Receiver internalReprOf(
+ AnnotationProvider provider, ExpressionTree receiverTree) {
+ return internalReprOf(provider, receiverTree, true);
+ }
+ /**
+ * We ignore operations such as widening and narrowing when computing the internal
+ * representation.
+ *
+ * @return the internal representation (as {@link Receiver}) of any {@link ExpressionTree}.
+ * Might contain {@link Unknown}.
+ */
+ public static Receiver internalReprOf(
+ AnnotationProvider provider,
+ ExpressionTree receiverTree,
+ boolean allowNonDeterministic) {
+ Receiver receiver;
+ switch (receiverTree.getKind()) {
+ case ARRAY_ACCESS:
+ ArrayAccessTree a = (ArrayAccessTree) receiverTree;
+ Receiver arrayAccessExpression = internalReprOf(provider, a.getExpression());
+ Receiver index = internalReprOf(provider, a.getIndex());
+ receiver = new ArrayAccess(InternalUtils.typeOf(a), arrayAccessExpression, index);
+ break;
+ case BOOLEAN_LITERAL:
+ case CHAR_LITERAL:
+ case DOUBLE_LITERAL:
+ case FLOAT_LITERAL:
+ case INT_LITERAL:
+ case LONG_LITERAL:
+ case NULL_LITERAL:
+ case STRING_LITERAL:
+ LiteralTree vn = (LiteralTree) receiverTree;
+ receiver = new ValueLiteral(InternalUtils.typeOf(receiverTree), vn.getValue());
+ break;
+ case NEW_ARRAY:
+ receiver =
+ new ArrayCreation(
+ InternalUtils.typeOf(receiverTree),
+ Collections.<Node>emptyList(),
+ Collections.<Node>emptyList());
+ break;
+ case METHOD_INVOCATION:
+ MethodInvocationTree mn = (MethodInvocationTree) receiverTree;
+ ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn);
+ if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterministic) {
+ List<Receiver> parameters = new ArrayList<>();
+ for (ExpressionTree p : mn.getArguments()) {
+ parameters.add(internalReprOf(provider, p));
+ }
+ Receiver methodReceiver;
+ if (ElementUtils.isStatic(invokedMethod)) {
+ methodReceiver = new ClassName(InternalUtils.typeOf(mn.getMethodSelect()));
+ } else {
+ methodReceiver = internalReprOf(provider, mn.getMethodSelect());
+ }
+ TypeMirror type = InternalUtils.typeOf(mn);
+ receiver = new MethodCall(type, invokedMethod, methodReceiver, parameters);
+ } else {
+ receiver = null;
+ }
+ break;
+ case MEMBER_SELECT:
+ receiver = internalRepOfMemberSelect(provider, (MemberSelectTree) receiverTree);
+ break;
+ case IDENTIFIER:
+ IdentifierTree identifierTree = (IdentifierTree) receiverTree;
+ TypeMirror typeOfId = InternalUtils.typeOf(identifierTree);
+ if (identifierTree.getName().contentEquals("this")
+ || identifierTree.getName().contentEquals("super")) {
+ receiver = new ThisReference(typeOfId);
+ break;
+ }
+ Element ele = TreeUtils.elementFromUse(identifierTree);
+ switch (ele.getKind()) {
+ case LOCAL_VARIABLE:
+ case RESOURCE_VARIABLE:
+ case EXCEPTION_PARAMETER:
+ case PARAMETER:
+ receiver = new LocalVariable(ele);
+ break;
+ case FIELD:
+ // Implicit access expression, such as "this" or a class name
+ Receiver fieldAccessExpression;
+ TypeMirror enclosingType = ElementUtils.enclosingClass(ele).asType();
+ if (ElementUtils.isStatic(ele)) {
+ fieldAccessExpression = new ClassName(enclosingType);
+ } else {
+ fieldAccessExpression = new ThisReference(enclosingType);
+ }
+ receiver =
+ new FieldAccess(
+ fieldAccessExpression, typeOfId, (VariableElement) ele);
+ break;
+ case CLASS:
+ case ENUM:
+ case ANNOTATION_TYPE:
+ case INTERFACE:
+ receiver = new ClassName(ele.asType());
+ break;
+ default:
+ receiver = null;
+ }
+ break;
+ default:
+ receiver = null;
+ }
+
+ if (receiver == null) {
+ receiver = new Unknown(InternalUtils.typeOf(receiverTree));
+ }
+ return receiver;
+ }
+
+ /**
+ * Returns the implicit receiver of ele.
+ *
+ * <p>Returns either a new ClassName or a new ThisReference depending on whether ele is static
+ * or not. The passed element must be a field, method, or class.
+ *
+ * @param ele field, method, or class
+ * @return either a new ClassName or a new ThisReference depending on whether ele is static or
+ * not
+ */
+ public static Receiver internalRepOfImplicitReceiver(Element ele) {
+ TypeMirror enclosingType = ElementUtils.enclosingClass(ele).asType();
+ if (ElementUtils.isStatic(ele)) {
+ return new ClassName(enclosingType);
+ } else {
+ return new ThisReference(enclosingType);
+ }
+ }
+
+ /**
+ * Returns either a new ClassName or ThisReference Receiver object for the enclosingType
+ *
+ * <p>The Tree should be an expression or a statement that does not have a receiver or an
+ * implicit receiver. For example, a local variable declaration.
+ *
+ * @param path TreePath to tree
+ * @param enclosingType type of the enclosing type
+ * @return a new ClassName or ThisReference that is a Receiver object for the enclosingType
+ */
+ public static Receiver internalRepOfPseudoReceiver(TreePath path, TypeMirror enclosingType) {
+ if (TreeUtils.isTreeInStaticScope(path)) {
+ return new ClassName(enclosingType);
+ } else {
+ return new ThisReference(enclosingType);
+ }
+ }
+
+ private static Receiver internalRepOfMemberSelect(
+ AnnotationProvider provider, MemberSelectTree memberSelectTree) {
+ TypeMirror expressionType = InternalUtils.typeOf(memberSelectTree.getExpression());
+ if (TreeUtils.isClassLiteral(memberSelectTree)) {
+ return new ClassName(expressionType);
+ }
+ Element ele = TreeUtils.elementFromUse(memberSelectTree);
+ switch (ele.getKind()) {
+ case METHOD:
+ case CONSTRUCTOR:
+ return internalReprOf(provider, memberSelectTree.getExpression());
+ case CLASS: // o instanceof MyClass.InnerClass
+ case ENUM:
+ case INTERFACE: // o instanceof MyClass.InnerInterface
+ case ANNOTATION_TYPE:
+ return new ClassName(expressionType);
+ case ENUM_CONSTANT:
+ case FIELD:
+ Receiver r = internalReprOf(provider, memberSelectTree.getExpression());
+ return new FieldAccess(r, ElementUtils.getType(ele), (VariableElement) ele);
+ default:
+ ErrorReporter.errorAbort(
+ "Unexpected element kind: %s element: %s", ele.getKind(), ele);
+ return null;
+ }
+ }
+
+ /**
+ * Returns Receiver objects for the formal parameters of the method in which path is enclosed.
+ *
+ * @param annotationProvider annotationProvider
+ * @param path TreePath that is enclosed by the method
+ * @return list of Receiver objects for the formal parameters of the method in which path is
+ * enclosed
+ */
+ public static List<Receiver> getParametersOfEnclosingMethod(
+ AnnotationProvider annotationProvider, TreePath path) {
+ MethodTree methodTree = TreeUtils.enclosingMethod(path);
+ if (methodTree == null) {
+ return null;
+ }
+ List<Receiver> internalArguments = new ArrayList<>();
+ for (VariableTree arg : methodTree.getParameters()) {
+ internalArguments.add(internalReprOf(annotationProvider, new LocalVariableNode(arg)));
+ }
+ return internalArguments;
+ }
+
+ public abstract static class Receiver {
protected final TypeMirror type;
public Receiver(TypeMirror type) {
@@ -196,38 +412,32 @@ public class FlowExpressions {
}
/**
- * Returns true if and only if the value this expression stands for
- * cannot be changed by a method call. This is the case for local
- * variables, the self reference as well as final field accesses for
- * whose receiver {@link #isUnmodifiableByOtherCode} is true.
+ * Returns true if and only if the value this expression stands for cannot be changed by a
+ * method call. This is the case for local variables, the self reference as well as final
+ * field accesses for whose receiver {@link #isUnmodifiableByOtherCode} is true.
*/
public abstract boolean isUnmodifiableByOtherCode();
- /**
- * @return True if and only if the two receiver are syntactically
- * identical.
- */
+ /** @return true if and only if the two receiver are syntactically identical */
public boolean syntacticEquals(Receiver other) {
return other == this;
}
/**
- * @return True if and only if this receiver contains a receiver that is
- * syntactically equal to {@code other}.
+ * @return true if and only if this receiver contains a receiver that is syntactically equal
+ * to {@code other}.
*/
public boolean containsSyntacticEqualReceiver(Receiver other) {
return syntacticEquals(other);
}
/**
- * Returns true if and only if {@code other} appear anywhere in this
- * receiver or an expression appears in this receiver such that
- * {@code other} might alias this expression, and that expression is
- * modifiable.
+ * Returns true if and only if {@code other} appears anywhere in this receiver or an
+ * expression appears in this receiver such that {@code other} might alias this expression,
+ * and that expression is modifiable.
*
- * <p>
- * This is always true, except for cases where the Java type information
- * prevents aliasing and none of the subexpressions can alias 'other'.
+ * <p>This is always true, except for cases where the Java type information prevents
+ * aliasing and none of the subexpressions can alias 'other'.
*/
public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
return this.equals(other) || store.canAlias(this, other);
@@ -252,8 +462,7 @@ public class FlowExpressions {
this.field = node.getElement();
}
- public FieldAccess(Receiver receiver, TypeMirror type,
- VariableElement fieldElement) {
+ public FieldAccess(Receiver receiver, TypeMirror type, VariableElement fieldElement) {
super(type);
this.receiver = receiver;
this.field = fieldElement;
@@ -273,8 +482,7 @@ public class FlowExpressions {
return false;
}
FieldAccess fa = (FieldAccess) obj;
- return fa.getField().equals(getField())
- && fa.getReceiver().equals(getReceiver());
+ return fa.getField().equals(getField()) && fa.getReceiver().equals(getReceiver());
}
@Override
@@ -290,8 +498,7 @@ public class FlowExpressions {
@Override
public boolean containsSyntacticEqualReceiver(Receiver other) {
- return syntacticEquals(other)
- || receiver.containsSyntacticEqualReceiver(other);
+ return syntacticEquals(other) || receiver.containsSyntacticEqualReceiver(other);
}
@Override
@@ -301,13 +508,17 @@ public class FlowExpressions {
}
FieldAccess fa = (FieldAccess) other;
return super.syntacticEquals(other)
- || fa.getField().equals(getField())
- && fa.getReceiver().syntacticEquals(getReceiver());
+ || (fa.getField().equals(getField())
+ && fa.getReceiver().syntacticEquals(getReceiver()));
}
@Override
public String toString() {
- return receiver + "." + field;
+ if (receiver instanceof ClassName) {
+ return receiver.getType() + "." + field;
+ } else {
+ return receiver + "." + field;
+ }
}
@Override
@@ -363,12 +574,10 @@ public class FlowExpressions {
}
/**
- * A ClassName represents the occurrence of a class as part of a static
- * field access or method invocation.
+ * A ClassName represents the occurrence of a class as part of a static field access or method
+ * invocation.
*/
public static class ClassName extends Receiver {
- protected Element element;
-
public ClassName(TypeMirror type) {
super(type);
}
@@ -389,7 +598,7 @@ public class FlowExpressions {
@Override
public String toString() {
- return getType().toString();
+ return getType().toString() + ".class";
}
@Override
@@ -447,7 +656,6 @@ public class FlowExpressions {
public boolean isUnmodifiableByOtherCode() {
return false;
}
-
}
public static class LocalVariable extends Receiver {
@@ -469,7 +677,17 @@ public class FlowExpressions {
return false;
}
LocalVariable other = (LocalVariable) obj;
- return other.element.equals(element);
+ VarSymbol vs = (VarSymbol) element;
+ VarSymbol vsother = (VarSymbol) other.element;
+ // Use TypeAnnotationUtils.unannotatedType(type).toString().equals(...) instead of Types.isSameType(...)
+ // because Types requires a processing environment, and FlowExpressions is
+ // designed to be independent of processing environment. See also
+ // calls to getType().toString() in FlowExpressions.
+ return vsother.name.contentEquals(vs.name)
+ && TypeAnnotationUtils.unannotatedType(vsother.type)
+ .toString()
+ .equals(TypeAnnotationUtils.unannotatedType(vs.type).toString())
+ && vsother.owner.toString().equals(vs.owner.toString());
}
public Element getElement() {
@@ -478,7 +696,11 @@ public class FlowExpressions {
@Override
public int hashCode() {
- return HashCodeUtils.hash(element);
+ VarSymbol vs = (VarSymbol) element;
+ return HashCodeUtils.hash(
+ vs.name.toString(),
+ TypeAnnotationUtils.unannotatedType(vs.type).toString(),
+ vs.owner.toString());
}
@Override
@@ -497,7 +719,7 @@ public class FlowExpressions {
return false;
}
LocalVariable l = (LocalVariable) other;
- return l.getElement().equals(getElement());
+ return l.equals(this);
}
@Override
@@ -542,11 +764,9 @@ public class FlowExpressions {
}
ValueLiteral other = (ValueLiteral) obj;
if (value == null) {
- return type.toString().equals(other.type.toString())
- && other.value == null;
+ return type.toString().equals(other.type.toString()) && other.value == null;
}
- return type.toString().equals(other.type.toString())
- && value.equals(other.value);
+ return type.toString().equals(other.type.toString()) && value.equals(other.value);
}
@Override
@@ -573,23 +793,24 @@ public class FlowExpressions {
public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
return false; // not modifiable
}
+
+ public Object getValue() {
+ return value;
+ }
}
- /**
- * A method call, typically a deterministic one. However, this is not
- * enforced and non-pure methods are also possible. It is the clients
- * responsibility to ensure that using non-deterministic methods is done in
- * a sound way. The CF allows non-deterministic methods to be used in
- * postconditions such as EnsuresNonNull.
- */
- public static class PureMethodCall extends Receiver {
+ /** A method call. */
+ public static class MethodCall extends Receiver {
protected final Receiver receiver;
protected final List<Receiver> parameters;
- protected final Element method;
+ protected final ExecutableElement method;
- public PureMethodCall(TypeMirror type, Element method,
- Receiver receiver, List<Receiver> parameters) {
+ public MethodCall(
+ TypeMirror type,
+ ExecutableElement method,
+ Receiver receiver,
+ List<Receiver> parameters) {
super(type);
this.receiver = receiver;
this.parameters = parameters;
@@ -612,6 +833,24 @@ public class FlowExpressions {
return false;
}
+ /** @return the method call receiver (for inspection only - do not modify) */
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ /**
+ * @return the method call parameters (for inspection only - do not modify any of the
+ * parameters)
+ */
+ public List<Receiver> getParameters() {
+ return Collections.unmodifiableList(parameters);
+ }
+
+ /** @return the ExecutableElement for the method call */
+ public ExecutableElement getElement() {
+ return method;
+ }
+
@Override
public boolean isUnmodifiableByOtherCode() {
return false;
@@ -624,10 +863,10 @@ public class FlowExpressions {
@Override
public boolean syntacticEquals(Receiver other) {
- if (!(other instanceof PureMethodCall)) {
+ if (!(other instanceof MethodCall)) {
return false;
}
- PureMethodCall otherMethod = (PureMethodCall) other;
+ MethodCall otherMethod = (MethodCall) other;
if (!receiver.syntacticEquals(otherMethod.receiver)) {
return false;
}
@@ -668,10 +907,10 @@ public class FlowExpressions {
@Override
public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof PureMethodCall)) {
+ if (obj == null || !(obj instanceof MethodCall)) {
return false;
}
- PureMethodCall other = (PureMethodCall) obj;
+ MethodCall other = (MethodCall) obj;
int i = 0;
for (Receiver p : parameters) {
if (!p.equals(other.parameters.get(i))) {
@@ -679,8 +918,7 @@ public class FlowExpressions {
}
i++;
}
- return receiver.equals(other.receiver)
- && method.equals(other.method);
+ return receiver.equals(other.receiver) && method.equals(other.method);
}
@Override
@@ -695,7 +933,11 @@ public class FlowExpressions {
@Override
public String toString() {
StringBuilder result = new StringBuilder();
- result.append(receiver.toString());
+ if (receiver instanceof ClassName) {
+ result.append(receiver.getType());
+ } else {
+ result.append(receiver);
+ }
result.append(".");
String methodName = method.getSimpleName().toString();
result.append(methodName);
@@ -713,9 +955,7 @@ public class FlowExpressions {
}
}
- /**
- * A deterministic method call.
- */
+ /** A deterministic method call. */
public static class ArrayAccess extends Receiver {
protected final Receiver receiver;
@@ -753,7 +993,8 @@ public class FlowExpressions {
@Override
public boolean containsSyntacticEqualReceiver(Receiver other) {
- return syntacticEquals(other) || receiver.syntacticEquals(other)
+ return syntacticEquals(other)
+ || receiver.syntacticEquals(other)
|| index.syntacticEquals(other);
}
@@ -801,4 +1042,102 @@ public class FlowExpressions {
return result.toString();
}
}
+
+ public static class ArrayCreation extends Receiver {
+
+ protected List<Node> dimensions;
+ protected List<Node> initializers;
+
+ public ArrayCreation(TypeMirror type, List<Node> dimensions, List<Node> initializers) {
+ super(type);
+ this.dimensions = dimensions;
+ this.initializers = initializers;
+ }
+
+ public List<Node> getDimensions() {
+ return dimensions;
+ }
+
+ public List<Node> getInitializers() {
+ return initializers;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends Receiver> clazz) {
+ for (Node n : dimensions) {
+ if (n.getClass().equals(clazz)) return true;
+ }
+ for (Node n : initializers) {
+ if (n.getClass().equals(clazz)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
+ result = prime * result + ((initializers == null) ? 0 : initializers.hashCode());
+ result = prime * result + HashCodeUtils.hash(getType().toString());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayCreation)) {
+ return false;
+ }
+ ArrayCreation other = (ArrayCreation) obj;
+ return this.dimensions.equals(other.getDimensions())
+ && this.initializers.equals(other.getInitializers())
+ && getType().toString().equals(other.getType().toString());
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ return this.equals(other);
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("new " + type);
+ if (!dimensions.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" (");
+ for (Node dim : dimensions) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(dim);
+ needComma = true;
+ }
+ sb.append(")");
+ }
+ if (!initializers.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" = {");
+ for (Node init : initializers) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(init);
+ needComma = true;
+ }
+ sb.append("}");
+ }
+ return sb.toString();
+ }
+ }
}