| package org.checkerframework.dataflow.expression; |
| |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.StringJoiner; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.type.TypeMirror; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.dataflow.analysis.Store; |
| import org.checkerframework.dataflow.util.PurityUtils; |
| import org.checkerframework.javacutil.AnnotationProvider; |
| |
| /** A call to a @Deterministic method. */ |
| public class MethodCall extends JavaExpression { |
| |
| /** The method being called. */ |
| protected final ExecutableElement method; |
| /** The receiver argument. */ |
| protected final JavaExpression receiver; |
| /** The arguments. */ |
| protected final List<JavaExpression> arguments; |
| |
| /** |
| * Creates a new MethodCall. |
| * |
| * @param type the type of the method call |
| * @param method the method being called |
| * @param receiver the receiver argument |
| * @param arguments the arguments |
| */ |
| public MethodCall( |
| TypeMirror type, |
| ExecutableElement method, |
| JavaExpression receiver, |
| List<JavaExpression> arguments) { |
| super(type); |
| this.receiver = receiver; |
| this.arguments = arguments; |
| this.method = method; |
| } |
| |
| /** |
| * Returns the ExecutableElement for the method call. |
| * |
| * @return the ExecutableElement for the method call |
| */ |
| public ExecutableElement getElement() { |
| return method; |
| } |
| |
| /** |
| * Returns the method call receiver (for inspection only - do not modify). |
| * |
| * @return the method call receiver (for inspection only - do not modify) |
| */ |
| public JavaExpression getReceiver() { |
| return receiver; |
| } |
| |
| /** |
| * Returns the method call arguments (for inspection only - do not modify any of the arguments). |
| * |
| * @return the method call arguments (for inspection only - do not modify any of the arguments) |
| */ |
| public List<JavaExpression> getArguments() { |
| return Collections.unmodifiableList(arguments); |
| } |
| |
| @Override |
| public boolean containsOfClass(Class<? extends JavaExpression> clazz) { |
| if (getClass() == clazz) { |
| return true; |
| } |
| if (receiver.containsOfClass(clazz)) { |
| return true; |
| } |
| for (JavaExpression p : arguments) { |
| if (p.containsOfClass(clazz)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isDeterministic(AnnotationProvider provider) { |
| return PurityUtils.isDeterministic(provider, method) |
| && listIsDeterministic(arguments, provider); |
| } |
| |
| @Override |
| public boolean isUnassignableByOtherCode() { |
| // There is no need to check that the method is deterministic, because a MethodCall is |
| // only created for deterministic methods. |
| return receiver.isUnmodifiableByOtherCode() |
| && arguments.stream().allMatch(JavaExpression::isUnmodifiableByOtherCode); |
| } |
| |
| @Override |
| public boolean isUnmodifiableByOtherCode() { |
| return isUnassignableByOtherCode(); |
| } |
| |
| @Override |
| public boolean syntacticEquals(JavaExpression je) { |
| if (!(je instanceof MethodCall)) { |
| return false; |
| } |
| MethodCall other = (MethodCall) je; |
| return method.equals(other.method) |
| && this.receiver.syntacticEquals(other.receiver) |
| && JavaExpression.syntacticEqualsList(this.arguments, other.arguments); |
| } |
| |
| @Override |
| public boolean containsSyntacticEqualJavaExpression(JavaExpression other) { |
| return syntacticEquals(other) |
| || receiver.containsSyntacticEqualJavaExpression(other) |
| || JavaExpression.listContainsSyntacticEqualJavaExpression(arguments, other); |
| } |
| |
| @Override |
| public boolean containsModifiableAliasOf(Store<?> store, JavaExpression other) { |
| if (receiver.containsModifiableAliasOf(store, other)) { |
| return true; |
| } |
| for (JavaExpression p : arguments) { |
| if (p.containsModifiableAliasOf(store, other)) { |
| return true; |
| } |
| } |
| return false; // the method call itself is not modifiable |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof MethodCall)) { |
| return false; |
| } |
| if (method.getKind() == ElementKind.CONSTRUCTOR) { |
| return false; |
| } |
| MethodCall other = (MethodCall) obj; |
| return method.equals(other.method) |
| && receiver.equals(other.receiver) |
| && arguments.equals(other.arguments); |
| } |
| |
| @Override |
| public int hashCode() { |
| if (method.getKind() == ElementKind.CONSTRUCTOR) { |
| return super.hashCode(); |
| } |
| return Objects.hash(method, receiver, arguments); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder preParen = new StringBuilder(); |
| if (receiver instanceof ClassName) { |
| preParen.append(receiver.getType()); |
| } else { |
| preParen.append(receiver); |
| } |
| preParen.append("."); |
| String methodName = method.getSimpleName().toString(); |
| preParen.append(methodName); |
| preParen.append("("); |
| StringJoiner result = new StringJoiner(", ", preParen, ")"); |
| for (JavaExpression argument : arguments) { |
| result.add(argument.toString()); |
| } |
| return result.toString(); |
| } |
| |
| @Override |
| public <R, P> R accept(JavaExpressionVisitor<R, P> visitor, P p) { |
| return visitor.visitMethodCall(this, p); |
| } |
| } |