All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.dataflow.expression.JavaExpression Maven / Gradle / Ivy

Go to download

dataflow-shaded is a dataflow framework based on the javac compiler. It differs from the org.checkerframework:dataflow artifact in two ways. First, the packages in this artifact have been renamed to org.checkerframework.shaded.*. Second, unlike the dataflow artifact, this artifact contains the dependencies it requires.

There is a newer version: 3.42.0-eisop5
Show newest version
package org.checkerframework.dataflow.expression;

import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.BinaryTree;
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.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import org.checkerframework.checker.interning.qual.EqualsMethod;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.Store;
import org.checkerframework.dataflow.cfg.node.ArrayAccessNode;
import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
import org.checkerframework.dataflow.cfg.node.BinaryOperationNode;
import org.checkerframework.dataflow.cfg.node.ClassNameNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.NarrowingConversionNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.StringConversionNode;
import org.checkerframework.dataflow.cfg.node.SuperNode;
import org.checkerframework.dataflow.cfg.node.ThisNode;
import org.checkerframework.dataflow.cfg.node.UnaryOperationNode;
import org.checkerframework.dataflow.cfg.node.ValueLiteralNode;
import org.checkerframework.dataflow.cfg.node.WideningConversionNode;
import org.checkerframework.javacutil.AnnotationProvider;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreePathUtil;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;

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.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

// The Lock Checker also supports "" as a JavaExpression, but that is implemented in the Lock
// Checker.
// There are no special subclasses (AST nodes) for "".
/**
 * This class represents a Java expression and its type. It does not represent all possible Java
 * expressions (for example, it does not represent a ternary conditional expression {@code ?:}; use
 * {@link org.checkerframework.dataflow.expression.Unknown} for unrepresentable expressions).
 *
 * 

This class's representation is like an AST: subparts are also expressions. For declared names * (fields, local variables, and methods), it also contains an Element. * *

Each subclass represents a different type of expression, such as {@link * org.checkerframework.dataflow.expression.MethodCall}, {@link * org.checkerframework.dataflow.expression.ArrayAccess}, {@link * org.checkerframework.dataflow.expression.LocalVariable}, etc. * * @see the syntax of * Java expressions supported by the Checker Framework */ public abstract class JavaExpression { /** The type of this expression. */ protected final TypeMirror type; /** * Create a JavaExpression. * * @param type the type of the expression */ protected JavaExpression(TypeMirror type) { assert type != null; this.type = type; } public TypeMirror getType() { return type; } public abstract boolean containsOfClass(Class clazz); public boolean containsUnknown() { return containsOfClass(Unknown.class); } /** * Returns true if the expression is deterministic. * * @param provider an annotation provider (a type factory) * @return true if this expression is deterministic */ public abstract boolean isDeterministic(AnnotationProvider provider); /** * Returns true if all the expressions in the list are deterministic. * * @param list the list whose elements to test * @param provider an annotation provider (a type factory) * @return true if all the expressions in the list are deterministic */ public static boolean listIsDeterministic( List list, AnnotationProvider provider) { return list.stream().allMatch(je -> je == null || je.isDeterministic(provider)); } /** * Returns true if and only if the value this expression stands for cannot be changed (with * respect to ==) by a method call. This is the case for local variables, the self reference, * final field accesses whose receiver is {@link #isUnassignableByOtherCode}, and operations * whose operands are all {@link #isUnmodifiableByOtherCode}. * * @see #isUnmodifiableByOtherCode */ public abstract boolean isUnassignableByOtherCode(); /** * Returns true if and only if the value this expression stands for cannot be changed by a * method call, including changes to any of its fields. * *

Approximately, this returns true if the expression is {@link #isUnassignableByOtherCode} * and its type is immutable. * * @see #isUnassignableByOtherCode */ public abstract boolean isUnmodifiableByOtherCode(); /** * Returns true if and only if the two Java expressions are syntactically identical. * *

This exists for use by {@link #containsSyntacticEqualJavaExpression}. * * @param je the other Java expression to compare to this one * @return true if and only if the two Java expressions are syntactically identical */ @EqualsMethod public abstract boolean syntacticEquals(JavaExpression je); /** * Returns true if the corresponding list elements satisfy {@link #syntacticEquals}. * * @param lst1 the first list to compare * @param lst2 the second list to compare * @return true if the corresponding list elements satisfy {@link #syntacticEquals} */ public static boolean syntacticEqualsList( List lst1, List lst2) { if (lst1.size() != lst2.size()) { return false; } for (int i = 0; i < lst1.size(); i++) { JavaExpression dim1 = lst1.get(i); JavaExpression dim2 = lst2.get(i); if (dim1 == null && dim2 == null) { continue; } else if (dim1 == null || dim2 == null) { return false; } else { if (!dim1.syntacticEquals(dim2)) { return false; } } } return true; } /** * Returns true if and only if this contains a JavaExpression that is syntactically equal to * {@code other}. * * @param other the JavaExpression to search for * @return true if and only if this contains a JavaExpression that is syntactically equal to * {@code other} */ public abstract boolean containsSyntacticEqualJavaExpression(JavaExpression other); /** * Returns true if the given list contains a JavaExpression that is syntactically equal to * {@code other}. * * @param list the list in which to search for a match * @param other the JavaExpression to search for * @return true if and only if the list contains a JavaExpression that is syntactically equal to * {@code other} */ public static boolean listContainsSyntacticEqualJavaExpression( List list, JavaExpression other) { return list.stream() .anyMatch(je -> je != null && je.containsSyntacticEqualJavaExpression(other)); } /** * Returns true if and only if {@code other} appears anywhere in this or an expression appears * in this such that {@code other} might alias this expression, and that expression is * modifiable. * *

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, JavaExpression other) { return this.equals(other) || store.canAlias(this, other); } /** * Format this verbosely, for debugging. * * @return a verbose string representation of this */ public String toStringDebug() { return String.format("%s(%s): %s", getClass().getSimpleName(), type, toString()); } /// /// Static methods /// /** * Returns the Java expression for a {@link FieldAccessNode}. The result may contain {@link * Unknown} as receiver. * * @param node the FieldAccessNode to convert to a JavaExpression * @return the {@link FieldAccess} or {@link ClassName} that corresponds to {@code node} */ public static JavaExpression fromNodeFieldAccess(FieldAccessNode node) { Node receiverNode = node.getReceiver(); String fieldName = node.getFieldName(); if (fieldName.equals("this")) { // The CFG represents "className.this" as a FieldAccessNode, but it isn't a field // access. return new ThisReference(receiverNode.getType()); } else if (fieldName.equals("class")) { // The CFG represents "className.class" as a FieldAccessNode; bit it is a class literal. return new ClassName(receiverNode.getType()); } JavaExpression receiver; if (node.isStatic()) { receiver = new ClassName(receiverNode.getType()); } else { receiver = fromNode(receiverNode); } return new FieldAccess(receiver, node); } /** * Returns the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}. * The result may contain {@link Unknown} as receiver. * * @param node the ArrayAccessNode to convert to a JavaExpression * @return the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}. * Can contain {@link Unknown} as receiver. */ public static ArrayAccess fromArrayAccess(ArrayAccessNode node) { JavaExpression array = fromNode(node.getArray()); JavaExpression index = fromNode(node.getIndex()); return new ArrayAccess(node.getType(), array, index); } /** * We ignore operations such as widening and narrowing when computing the internal * representation. * * @param receiverNode a node to convert to a JavaExpression * @return the internal representation of the given node. Might contain {@link Unknown}. */ public static JavaExpression fromNode(Node receiverNode) { JavaExpression result = null; if (receiverNode instanceof FieldAccessNode) { result = fromNodeFieldAccess((FieldAccessNode) receiverNode); } else if (receiverNode instanceof ThisNode) { result = new ThisReference(receiverNode.getType()); } else if (receiverNode instanceof SuperNode) { result = new ThisReference(receiverNode.getType()); } else if (receiverNode instanceof LocalVariableNode) { LocalVariableNode lv = (LocalVariableNode) receiverNode; result = new LocalVariable(lv); } else if (receiverNode instanceof ArrayAccessNode) { ArrayAccessNode a = (ArrayAccessNode) receiverNode; result = fromArrayAccess(a); } else if (receiverNode instanceof StringConversionNode) { // ignore string conversion return fromNode(((StringConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof WideningConversionNode) { // ignore widening return fromNode(((WideningConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof NarrowingConversionNode) { // ignore narrowing return fromNode(((NarrowingConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof UnaryOperationNode) { UnaryOperationNode uopn = (UnaryOperationNode) receiverNode; return new UnaryOperation(uopn, fromNode(uopn.getOperand())); } else if (receiverNode instanceof BinaryOperationNode) { BinaryOperationNode bopn = (BinaryOperationNode) receiverNode; return new BinaryOperation( bopn, fromNode(bopn.getLeftOperand()), fromNode(bopn.getRightOperand())); } else if (receiverNode instanceof ClassNameNode) { ClassNameNode cn = (ClassNameNode) receiverNode; result = new ClassName(cn.getType()); } else if (receiverNode instanceof ValueLiteralNode) { ValueLiteralNode vn = (ValueLiteralNode) receiverNode; result = new ValueLiteral(vn.getType(), vn); } else if (receiverNode instanceof ArrayCreationNode) { ArrayCreationNode an = (ArrayCreationNode) receiverNode; List<@Nullable JavaExpression> dimensions = CollectionsPlume.mapList(JavaExpression::fromNode, an.getDimensions()); List initializers = CollectionsPlume.mapList(JavaExpression::fromNode, an.getInitializers()); result = new ArrayCreation(an.getType(), dimensions, initializers); } else if (receiverNode instanceof MethodInvocationNode) { MethodInvocationNode mn = (MethodInvocationNode) receiverNode; MethodInvocationTree t = mn.getTree(); if (t == null) { throw new BugInCF("Unexpected null tree for node: " + mn); } assert TreeUtils.isUseOfElement(t) : "@AssumeAssertion(nullness): tree kind"; ExecutableElement invokedMethod = TreeUtils.elementFromUse(t); // Note that the method might be nondeterministic. List parameters = CollectionsPlume.mapList(JavaExpression::fromNode, mn.getArguments()); JavaExpression methodReceiver; if (ElementUtils.isStatic(invokedMethod)) { methodReceiver = new ClassName(mn.getTarget().getReceiver().getType()); } else { methodReceiver = fromNode(mn.getTarget().getReceiver()); } result = new MethodCall(mn.getType(), invokedMethod, methodReceiver, parameters); } if (result == null) { result = new Unknown(receiverNode); } return result; } /** * Converts a javac {@link ExpressionTree} to a CF JavaExpression. The result might contain * {@link Unknown}. * *

We ignore operations such as widening and narrowing when computing the JavaExpression. * * @param tree a javac tree * @return a JavaExpression for the given javac tree */ public static JavaExpression fromTree(ExpressionTree tree) { JavaExpression result; switch (tree.getKind()) { case ARRAY_ACCESS: ArrayAccessTree a = (ArrayAccessTree) tree; JavaExpression arrayAccessExpression = fromTree(a.getExpression()); JavaExpression index = fromTree(a.getIndex()); result = new ArrayAccess(TreeUtils.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) tree; result = new ValueLiteral(TreeUtils.typeOf(tree), vn.getValue()); break; case NEW_ARRAY: NewArrayTree newArrayTree = (NewArrayTree) tree; List<@Nullable JavaExpression> dimensions; if (newArrayTree.getDimensions() == null) { dimensions = Collections.emptyList(); } else { dimensions = new ArrayList<>(newArrayTree.getDimensions().size()); for (ExpressionTree dimension : newArrayTree.getDimensions()) { dimensions.add(fromTree(dimension)); } } List initializers; if (newArrayTree.getInitializers() == null) { initializers = Collections.emptyList(); } else { initializers = new ArrayList<>(newArrayTree.getInitializers().size()); for (ExpressionTree initializer : newArrayTree.getInitializers()) { initializers.add(fromTree(initializer)); } } result = new ArrayCreation(TreeUtils.typeOf(tree), dimensions, initializers); break; case METHOD_INVOCATION: MethodInvocationTree mn = (MethodInvocationTree) tree; assert TreeUtils.isUseOfElement(mn) : "@AssumeAssertion(nullness): tree kind"; ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn); // Note that the method might be nondeterministic. List parameters = CollectionsPlume.mapList(JavaExpression::fromTree, mn.getArguments()); JavaExpression methodReceiver; if (ElementUtils.isStatic(invokedMethod)) { @SuppressWarnings( "nullness:assignment" // enclosingTypeElement(ExecutableElement): // @NonNull ) @NonNull TypeElement methodType = ElementUtils.enclosingTypeElement(invokedMethod); methodReceiver = new ClassName(methodType.asType()); } else { methodReceiver = getReceiver(mn); } TypeMirror resultType = TreeUtils.typeOf(mn); result = new MethodCall(resultType, invokedMethod, methodReceiver, parameters); break; case MEMBER_SELECT: result = fromMemberSelect((MemberSelectTree) tree); break; case IDENTIFIER: IdentifierTree identifierTree = (IdentifierTree) tree; TypeMirror typeOfId = TreeUtils.typeOf(identifierTree); Name identifierName = identifierTree.getName(); if (identifierName.contentEquals("this") || identifierName.contentEquals("super")) { result = new ThisReference(typeOfId); break; } assert TreeUtils.isUseOfElement(identifierTree) : "@AssumeAssertion(nullness): tree kind"; Element ele = TreeUtils.elementFromUse(identifierTree); if (ele == null) { result = null; } else if (ElementUtils.isTypeElement(ele)) { result = new ClassName(ele.asType()); } else { result = fromVariableElement(typeOfId, (VariableElement) ele, identifierTree); } break; case UNARY_PLUS: return fromTree(((UnaryTree) tree).getExpression()); case BITWISE_COMPLEMENT: case LOGICAL_COMPLEMENT: case POSTFIX_DECREMENT: case POSTFIX_INCREMENT: case PREFIX_DECREMENT: case PREFIX_INCREMENT: case UNARY_MINUS: JavaExpression operand = fromTree(((UnaryTree) tree).getExpression()); return new UnaryOperation(TreeUtils.typeOf(tree), tree.getKind(), operand); case CONDITIONAL_AND: case CONDITIONAL_OR: case DIVIDE: case EQUAL_TO: case GREATER_THAN: case GREATER_THAN_EQUAL: case LEFT_SHIFT: case LESS_THAN: case LESS_THAN_EQUAL: case MINUS: case MULTIPLY: case NOT_EQUAL_TO: case OR: case PLUS: case REMAINDER: case RIGHT_SHIFT: case UNSIGNED_RIGHT_SHIFT: case XOR: BinaryTree binaryTree = (BinaryTree) tree; JavaExpression left = fromTree(binaryTree.getLeftOperand()); JavaExpression right = fromTree(binaryTree.getRightOperand()); return new BinaryOperation(TreeUtils.typeOf(tree), tree.getKind(), left, right); default: result = null; } if (result == null) { result = new Unknown(tree); } return result; } /** * Returns the Java expression corresponding to the given variable tree {@code tree}. * * @param tree a variable tree * @return a JavaExpression for {@code tree} */ public static JavaExpression fromVariableTree(VariableTree tree) { return fromVariableElement( TreeUtils.typeOf(tree), TreeUtils.elementFromDeclaration(tree), tree); } /** * Returns the Java expression corresponding to the given variable element {@code ele}. * * @param typeOfEle the type of {@code ele} * @param ele element whose JavaExpression is returned * @param tree the tree for the variable * @return the Java expression corresponding to the given variable element {@code ele} */ private static JavaExpression fromVariableElement( TypeMirror typeOfEle, @Nullable VariableElement ele, Tree tree) { if (ele == null) { return new Unknown(tree); } switch (ele.getKind()) { case LOCAL_VARIABLE: case RESOURCE_VARIABLE: case EXCEPTION_PARAMETER: case PARAMETER: return new LocalVariable(ele); case FIELD: case ENUM_CONSTANT: // Implicit access expression, such as "this" or a class name JavaExpression fieldAccessExpression; @SuppressWarnings("nullness:dereference.of.nullable") // a field has enclosing class TypeMirror enclosingTypeElement = ElementUtils.enclosingTypeElement(ele).asType(); if (ElementUtils.isStatic(ele)) { fieldAccessExpression = new ClassName(enclosingTypeElement); } else { fieldAccessExpression = new ThisReference(enclosingTypeElement); } return new FieldAccess(fieldAccessExpression, typeOfEle, ele); default: if (ElementUtils.isBindingVariable(ele)) { return new LocalVariable(ele); } throw new BugInCF( "Unexpected kind of VariableTree: kind: %s element: %s", ele.getKind(), ele); } } /** * Creates a JavaExpression from the {@code memberSelectTree}. * * @param memberSelectTree tree * @return a JavaExpression for {@code memberSelectTree} */ private static JavaExpression fromMemberSelect(MemberSelectTree memberSelectTree) { TypeMirror expressionType = TreeUtils.typeOf(memberSelectTree.getExpression()); if (TreeUtils.isClassLiteral(memberSelectTree)) { // the identifier is "class" return new ClassName(expressionType); } if (TreeUtils.isExplicitThisDereference(memberSelectTree)) { // the identifier is "class" return new ThisReference(expressionType); } assert TreeUtils.isUseOfElement(memberSelectTree) : "@AssumeAssertion(nullness): tree kind"; Element ele = TreeUtils.elementFromUse(memberSelectTree); if (ElementUtils.isTypeElement(ele)) { // o instanceof MyClass.InnerClass // o instanceof MyClass.InnerInterface TypeMirror selectType = TreeUtils.typeOf(memberSelectTree); return new ClassName(selectType); } switch (ele.getKind()) { case METHOD: case CONSTRUCTOR: return fromTree(memberSelectTree.getExpression()); case ENUM_CONSTANT: case FIELD: TypeMirror fieldType = TreeUtils.typeOf(memberSelectTree); JavaExpression je = fromTree(memberSelectTree.getExpression()); return new FieldAccess(je, fieldType, (VariableElement) ele); default: throw new BugInCF("Unexpected element kind: %s element: %s", ele.getKind(), ele); } } /** * Returns the parameters of {@code methodEle} as {@link LocalVariable}s. * * @param methodEle the method element * @return list of parameters as {@link LocalVariable}s */ public static List getParametersAsLocalVariables(ExecutableElement methodEle) { return CollectionsPlume.mapList(LocalVariable::new, methodEle.getParameters()); } /** * Returns the parameters of {@code methodEle} as {@link FormalParameter}s. * * @param methodEle the method element * @return list of parameters as {@link FormalParameter}s */ public static List getFormalParameters(ExecutableElement methodEle) { List parameters = new ArrayList<>(methodEle.getParameters().size()); int oneBasedIndex = 1; for (VariableElement variableElement : methodEle.getParameters()) { parameters.add(new FormalParameter(oneBasedIndex, variableElement)); oneBasedIndex++; } return parameters; } /// /// Obtaining the receiver /// /** * Returns the receiver of the given invocation. * * @param accessTree a method or constructor invocation * @return the receiver of the given invocation */ public static JavaExpression getReceiver(ExpressionTree accessTree) { // TODO: Handle field accesses too? assert accessTree instanceof MethodInvocationTree || accessTree instanceof NewClassTree; ExpressionTree receiverTree = TreeUtils.getReceiverTree(accessTree); if (receiverTree != null) { return fromTree(receiverTree); } else { Element ele = TreeUtils.elementFromUse(accessTree); if (ele == null) { throw new BugInCF("TreeUtils.elementFromUse(" + accessTree + ") => null"); } return getImplicitReceiver(ele); } } /** * Returns the implicit receiver of ele. * *

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. * *

When this returns a ThisReference, its type is the class that declares {@code ele}, which * is not necessarily the type of {@code this} at the invocation site. * * @param ele a field, method, or class * @return either a new ClassName or a new ThisReference depending on whether ele is static or * not */ public static JavaExpression getImplicitReceiver(Element ele) { TypeElement enclosingTypeElement = ElementUtils.enclosingTypeElement(ele); if (enclosingTypeElement == null) { throw new BugInCF("getImplicitReceiver's arg has no enclosing type: " + ele); } TypeMirror enclosingType = enclosingTypeElement.asType(); if (ElementUtils.isStatic(ele)) { return new ClassName(enclosingType); } else { return new ThisReference(enclosingType); } } /** * Returns either a new ClassName or ThisReference JavaExpression object for the enclosingType. * *

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 a tree path * @param enclosingType type of the enclosing type * @return a new {@link ClassName} or {@link ThisReference} that is a JavaExpression object for * the enclosingType */ public static JavaExpression getPseudoReceiver(TreePath path, TypeMirror enclosingType) { if (TreePathUtil.isTreeInStaticScope(path)) { return new ClassName(enclosingType); } else { return new ThisReference(enclosingType); } } /** * Accept method of the visitor pattern. * * @param visitor the visitor to be applied to this JavaExpression * @param p the parameter for this operation * @param result type of the operation * @param

parameter type * @return the result of visiting this */ public abstract R accept(JavaExpressionVisitor visitor, P p); /** * Viewpoint-adapts {@code this} to a field access with receiver {@code receiver}. * * @param receiver receiver of the field access * @return viewpoint-adapted version of this */ public JavaExpression atFieldAccess(JavaExpression receiver) { return ViewpointAdaptJavaExpression.viewpointAdapt(this, receiver); } /** * Viewpoint-adapts {@code this} to the {@code methodTree} by converting any {@code * FormalParameter} into {@code LocalVariable}s. * * @param methodTree method declaration tree * @return viewpoint-adapted version of this */ public final JavaExpression atMethodBody(MethodTree methodTree) { List parametersJe = CollectionsPlume.mapList( (VariableTree param) -> new LocalVariable(TreeUtils.elementFromDeclaration(param)), methodTree.getParameters()); return ViewpointAdaptJavaExpression.viewpointAdapt(this, parametersJe); } /** * Viewpoint-adapts {@code this} to the {@code methodInvocationTree}. * * @param methodInvocationTree method invocation * @return viewpoint-adapted version of this */ public final JavaExpression atMethodInvocation(MethodInvocationTree methodInvocationTree) { JavaExpression receiverJe = JavaExpression.getReceiver(methodInvocationTree); List argumentsJe = argumentTreesToJavaExpressions( TreeUtils.elementFromUse(methodInvocationTree), methodInvocationTree.getArguments()); return ViewpointAdaptJavaExpression.viewpointAdapt(this, receiverJe, argumentsJe); } /** * Viewpoint-adapts {@code this} to the {@code invocationNode}. * * @param invocationNode method invocation * @return viewpoint-adapted version of this */ public final JavaExpression atMethodInvocation(MethodInvocationNode invocationNode) { JavaExpression receiverJe = JavaExpression.fromNode(invocationNode.getTarget().getReceiver()); List argumentsJe = CollectionsPlume.mapList(JavaExpression::fromNode, invocationNode.getArguments()); return ViewpointAdaptJavaExpression.viewpointAdapt(this, receiverJe, argumentsJe); } /** * Viewpoint-adapts {@code this} to the {@code newClassTree}. * * @param newClassTree constructor invocation * @return viewpoint-adapted version of this */ public JavaExpression atConstructorInvocation(NewClassTree newClassTree) { JavaExpression receiverJe = JavaExpression.getReceiver(newClassTree); List argumentsJe = argumentTreesToJavaExpressions( TreeUtils.elementFromUse(newClassTree), newClassTree.getArguments()); return ViewpointAdaptJavaExpression.viewpointAdapt(this, receiverJe, argumentsJe); } /** * Converts method or constructor arguments from Trees to JavaExpressions, accounting for * varargs. * * @param method the method or constructor being invoked * @param argTrees the arguments to the method or constructor * @return the arguments, as JavaExpressions */ private static List argumentTreesToJavaExpressions( ExecutableElement method, List argTrees) { if (isVarArgsInvocation(method, argTrees)) { List result = new ArrayList<>(method.getParameters().size()); for (int i = 0; i < method.getParameters().size() - 1; i++) { result.add(JavaExpression.fromTree(argTrees.get(i))); } List varargArgs = new ArrayList<>(argTrees.size() - method.getParameters().size() + 1); for (int i = method.getParameters().size() - 1; i < argTrees.size(); i++) { varargArgs.add(JavaExpression.fromTree(argTrees.get(i))); } Element varargsElement = method.getParameters().get(method.getParameters().size() - 1); TypeMirror tm = ElementUtils.getType(varargsElement); result.add(new ArrayCreation(tm, Collections.emptyList(), varargArgs)); return result; } return CollectionsPlume.mapList(JavaExpression::fromTree, argTrees); } /** * Returns true if method is a varargs method or constructor and its varargs arguments are not * passed in an array. * * @param method the method or constructor * @param args the arguments at the call site * @return true if method is a varargs method and its varargs arguments are not passed in an * array */ private static boolean isVarArgsInvocation( ExecutableElement method, List args) { if (!method.isVarArgs()) { return false; } if (method.getParameters().size() != args.size()) { return true; } TypeMirror lastArgType = TreeUtils.typeOf(args.get(args.size() - 1)); if (lastArgType.getKind() != TypeKind.ARRAY) { return true; } List paramElts = method.getParameters(); VariableElement lastParamElt = paramElts.get(paramElts.size() - 1); return TypesUtils.getArrayDepth(ElementUtils.getType(lastParamElt)) != TypesUtils.getArrayDepth(lastArgType); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy