diff options
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.java | 563 |
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(); + } + } } |