| package org.checkerframework.dataflow.cfg.node; |
| |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.util.TreePath; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Objects; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodParameterContext; |
| import org.checkerframework.javacutil.TreeUtils; |
| import org.plumelib.util.StringsPlume; |
| |
| /** |
| * A node for method invocation. |
| * |
| * <pre> |
| * <em>target(arg1, arg2, ...)</em> |
| * </pre> |
| * |
| * CFGs may contain {@link MethodInvocationNode}s that correspond to no AST {@link Tree}, in which |
| * case, the tree field will be null. |
| */ |
| public class MethodInvocationNode extends Node { |
| |
| /** The tree for the method invocation. */ |
| protected final @Nullable MethodInvocationTree tree; |
| |
| /** |
| * The MethodAccessNode for the method being invoked. Includes the receiver if any. For a static |
| * method, the receiver may be a class name. |
| */ |
| protected final MethodAccessNode target; |
| |
| /** The arguments of the method invocation. */ |
| protected final List<Node> arguments; |
| |
| /** The tree path to the method invocation. */ |
| protected final TreePath treePath; |
| |
| /** |
| * If this MethodInvocationNode is a node for an {@link Iterator#next()} desugared from an |
| * enhanced for loop, then the {@code iterExpression} field is the expression in the for loop, |
| * e.g., {@code iter} in {@code for(Object o: iter}. |
| */ |
| protected @Nullable ExpressionTree iterableExpression; |
| |
| /** |
| * Create a MethodInvocationNode. |
| * |
| * @param tree for the method invocation |
| * @param target the MethodAccessNode for the method being invoked |
| * @param arguments arguments of the method invocation |
| * @param treePath path to the method invocation |
| */ |
| public MethodInvocationNode( |
| @Nullable MethodInvocationTree tree, |
| MethodAccessNode target, |
| List<Node> arguments, |
| TreePath treePath) { |
| super(tree != null ? TreeUtils.typeOf(tree) : target.getMethod().getReturnType()); |
| this.tree = tree; |
| this.target = target; |
| this.arguments = arguments; |
| this.treePath = treePath; |
| |
| // set assignment contexts for parameters |
| int i = 0; |
| for (Node arg : arguments) { |
| AssignmentContext ctx = new MethodParameterContext(target.getMethod(), i++); |
| arg.setAssignmentContext(ctx); |
| } |
| } |
| |
| public MethodInvocationNode(MethodAccessNode target, List<Node> arguments, TreePath treePath) { |
| this(null, target, arguments, treePath); |
| } |
| |
| public MethodAccessNode getTarget() { |
| return target; |
| } |
| |
| public List<Node> getArguments() { |
| return arguments; |
| } |
| |
| public Node getArgument(int i) { |
| return arguments.get(i); |
| } |
| |
| public TreePath getTreePath() { |
| return treePath; |
| } |
| |
| /** |
| * If this MethodInvocationNode is a node for an {@link Iterator#next()} desugared from an |
| * enhanced for loop, then return the expression in the for loop, e.g., {@code iter} in {@code |
| * for(Object o: iter}. Otherwise, return null. |
| * |
| * @return the iter expression, or null if this is not a {@link Iterator#next()} from an enhanced |
| * for loop |
| */ |
| public @Nullable ExpressionTree getIterableExpression() { |
| return iterableExpression; |
| } |
| |
| /** |
| * Set the iterable expression from a for loop. |
| * |
| * @param iterableExpression iterable expression |
| * @see #getIterableExpression() |
| */ |
| public void setIterableExpression(@Nullable ExpressionTree iterableExpression) { |
| this.iterableExpression = iterableExpression; |
| } |
| |
| @Override |
| public @Nullable MethodInvocationTree getTree() { |
| return tree; |
| } |
| |
| @Override |
| public <R, P> R accept(NodeVisitor<R, P> visitor, P p) { |
| return visitor.visitMethodInvocation(this, p); |
| } |
| |
| @Override |
| public String toString() { |
| return target + "(" + StringsPlume.join(", ", arguments) + ")"; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object obj) { |
| if (!(obj instanceof MethodInvocationNode)) { |
| return false; |
| } |
| MethodInvocationNode other = (MethodInvocationNode) obj; |
| |
| return getTarget().equals(other.getTarget()) && getArguments().equals(other.getArguments()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(target, arguments); |
| } |
| |
| @Override |
| public Collection<Node> getOperands() { |
| List<Node> list = new ArrayList<>(1 + arguments.size()); |
| list.add(target); |
| list.addAll(arguments); |
| return list; |
| } |
| } |