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

framework.src.org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenes Maven / Gradle / Ivy

Go to download

Checker Qual is the set of annotations (qualifiers) and supporting classes used by the Checker Framework to type check Java source code. Please see artifact: org.checkerframework:checker

There is a newer version: 3.45.0
Show newest version
package org.checkerframework.common.wholeprograminference;

import java.util.List;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;

import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.ObjectCreationNode;
import org.checkerframework.dataflow.cfg.node.ReturnNode;
import org.checkerframework.framework.qual.IgnoreInWholeProgramInference;
import org.checkerframework.framework.qual.TypeUseLocation;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.qualframework.qual.QualifierKey;

import annotations.el.AClass;
import annotations.el.AField;
import annotations.el.AMethod;
import annotations.util.JVMNames;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type.ClassType;

/**
 * WholeProgramInferenceScenes is an implementation of
 * {@link org.checkerframework.common.wholeprograminference.WholeProgramInference}
 * that uses a helper class
 * ({@link org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenesHelper})
 * that manipulates .jaif files to perform whole-program inference.
 * 

* Calling an update* method * ({@link #updateInferredFieldType updateInferredFieldType}, * {@link #updateInferredMethodParameterTypes updateInferredMethodParameterTypes}, * {@link #updateInferredParameterType updateInferredParameterType}, or * {@link #updateInferredMethodReturnType updateInferredMethodReturnType}) * replaces the currently-stored type for an element in a {@link annotations.el.AScene}, if any, * by the LUB of it and the update method's argument. *

* This class does not perform inference for an element if the element has * explicit annotations: an update* method ignores an * explicitly annotated field, method return, or method parameter when * passed as an argument. *

* In addition, whole program inference ignores inferred types in a few scenarios. * When discovering a use, if: *

    *
  1. The inferred type of an element that should be written into a .jaif * file is a subtype of the upper bounds of this element's currently-written * type on the source code.
  2. *
  3. The annotation annotates a null literal, except when * doing inference for the NullnessChecker. (The rationale for this * is that null is a frequently-used default value, and * it would be undesirable to compute any inferred type if * null were the only value passed as an argument.)
  4. *
* When outputting a .jaif file, if: *
    *
  1. The @Target annotation does not permit the annotation to be * written at this location.
  2. *
  3. The inferred has the @InvisibleQualifier meta-annotation.
  4. *
  5. The resulting type would be defaulted or implicited — that is, if * omitting it has the same effect as writing it.
  6. *
  7. Special case: The * {@link QualifierKey} * won't be written into .jaif. (This will probably change once we support * type-checkers that use a CheckerAdapter.)
  8. *
* @author pbsf */ // TODO: We could add an option to update the type of explicitly annotated // elements, but this currently is not recommended since the // insert-annotations-to-source tool, which adds annotations from .jaif files // into source code, adds annotations on top of existing // annotations. See https://github.com/typetools/annotation-tools/issues/105 . // TODO: Ensure that annotations are inserted deterministically into .jaif // files. This is important for debugging and comparison; otherwise running // the whole-program inference on the same set of files can yield different // results (order of annotations). public class WholeProgramInferenceScenes implements WholeProgramInference { private final WholeProgramInferenceScenesHelper helper; public WholeProgramInferenceScenes(boolean ignoreNullAssignments) { helper = new WholeProgramInferenceScenesHelper(ignoreNullAssignments); } /** * Updates the parameter types of the constructor created by objectCreationNode * based on arguments to the constructor. *

* For each parameter in constructorElt: *

    *
  • If the Scene does not contain an annotated type for that * parameter, then the type of the respective value passed as argument * in the object creation call objectCreationNode will be added to the * parameter in the Scene.
  • *
  • If the Scene previously contained an annotated type for that * parameter, then its new type will be the LUB between the previous * type and the type of the respective value passed as argument in the * object creation call.
  • *
*

* @param objectCreationNode the new Object() node. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the constructor's parameters' types. */ @Override public void updateInferredConstructorParameterTypes( ObjectCreationNode objectCreationNode, ExecutableElement constructorElt, AnnotatedTypeFactory atf) { ClassSymbol classSymbol = getEnclosingClassSymbol(objectCreationNode.getTree()); if (classSymbol == null) { // TODO: Handle anonymous classes. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 return; } String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); String methodName = JVMNames.getJVMMethodName(constructorElt); AMethod method = clazz.methods.vivify(methodName); List arguments = objectCreationNode.getArguments(); updateInferredExecutableParameterTypes(constructorElt, atf, jaifPath, method, arguments); } /** * Updates the parameter types of the method {@code methodElt} in the Scene * of the method's enclosing class based on the overridden method * {@code overriddenMethod} parameter types. *

* For each method parameter in methodElt: *

    *
  • If the Scene does not contain an annotated type for that * parameter, then the type of the respective parameter on the * overridden method will be added to the parameter in the Scene.
  • *
  • If the Scene previously contained an annotated type for that * parameter, then its new type will be the LUB between the previous * type and the type of the respective parameter on the overridden * method.
  • *
*

* @param methodTree the tree of the method that contains the parameter. * @param methodElt the element of the method. * @param overriddenMethod the AnnotatedExecutableType of the overridden * method. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the parameter type. */ @Override public void updateInferredMethodParameterTypes( MethodTree methodTree, ExecutableElement methodElt, AnnotatedExecutableType overriddenMethod, AnnotatedTypeFactory atf) { ClassSymbol classSymbol = getEnclosingClassSymbol(methodTree); String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); String methodName = JVMNames.getJVMMethodName(methodElt); AMethod method = clazz.methods.vivify(methodName); for (int i = 0; i < overriddenMethod.getParameterTypes().size(); i++) { VariableElement ve = methodElt.getParameters().get(i); AnnotatedTypeMirror paramATM = atf.getAnnotatedType(ve); AnnotatedTypeMirror argATM = overriddenMethod.getParameterTypes().get(i); AField param = method.parameters.vivify(i); helper.updateAnnotationSetInScene( param.type, atf, jaifPath, argATM, paramATM, TypeUseLocation.PARAMETER); } } /** * Updates the parameter types of the method methodElt in the Scene of the * receiverTree's enclosing class based on the arguments to the method. *

* For each method parameter in methodElt: *

    *
  • If the Scene does not contain an annotated type for that * parameter, then the type of the respective value passed as argument * in the method call methodInvNode will be added to the parameter in * the Scene.
  • *
  • If the Scene previously contained an annotated type for that * parameter, then its new type will be the LUB between the previous * type and the type of the respective value passed as argument in the * method call.
  • *
*

* @param methodInvNode the node representing a method invocation. * @param receiverTree the Tree of the class that contains the method being * invoked. * @param methodElt the element of the method being invoked. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the method parameters' types. */ @Override public void updateInferredMethodParameterTypes( MethodInvocationNode methodInvNode, Tree receiverTree, ExecutableElement methodElt, AnnotatedTypeFactory atf) { if (receiverTree == null) { // TODO: Method called from static context. // I struggled to obtain the ClassTree of a method called // from a static context and currently I'm ignoring it. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 return; } ClassSymbol classSymbol = getEnclosingClassSymbol(receiverTree); if (classSymbol == null) { // TODO: Handle anonymous classes. // Also struggled to obtain the ClassTree from an anonymous class. // Ignoring it for now. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 return; } // TODO: We must handle cases where the method is declared on a superclass. // Currently we are ignoring them. See ElementUtils#getSuperTypes. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 if (!classSymbol.getEnclosedElements().contains(methodElt)) return; String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); String methodName = JVMNames.getJVMMethodName(methodElt); AMethod method = clazz.methods.vivify(methodName); List arguments = methodInvNode.getArguments(); updateInferredExecutableParameterTypes(methodElt, atf, jaifPath, method, arguments); } /** * Helper method for updating parameter types based on calls to a method or constructor. */ private void updateInferredExecutableParameterTypes(ExecutableElement methodElt, AnnotatedTypeFactory atf, String jaifPath, AMethod method, List arguments) { for (int i = 0; i < arguments.size(); i++) { VariableElement ve = methodElt.getParameters().get(i); AnnotatedTypeMirror paramATM = atf.getAnnotatedType(ve); Node arg = arguments.get(i); Tree treeNode = arg.getTree(); if (treeNode == null) { // TODO: Handle variable-length list as parameter. // An ArrayCreationNode with a null tree is created when the // parameter is a variable-length list. We are ignoring it for now. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 continue; } AnnotatedTypeMirror argATM = atf.getAnnotatedType(treeNode); AField param = method.parameters.vivify(i); helper.updateAnnotationSetInScene( param.type, atf, jaifPath, argATM, paramATM, TypeUseLocation.PARAMETER); } } /** * Updates the parameter type represented by lhs of the method methodTree * in the Scene of the receiverTree's enclosing class based on assignments * to the parameter inside the method body. *

    *
  • If the Scene does not contain an annotated type for that * parameter, then the type of the respective value passed as argument * in the method call methodInvNode will be added to the parameter in * the Scene.
  • *
  • If the Scene previously contained an annotated type for that * parameter, then its new type will be the LUB between the previous * type and the type of the respective value passed as argument in the * method call.
  • *
*

* @param lhs the node representing the parameter. * @param rhs the node being assigned to the parameter. * @param classTree the tree of the class that contains the parameter. * @param methodTree the tree of the method that contains the parameter. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the parameter type. */ @Override public void updateInferredParameterType( LocalVariableNode lhs, Node rhs, ClassTree classTree, MethodTree methodTree, AnnotatedTypeFactory atf) { ClassSymbol classSymbol = getEnclosingClassSymbol(classTree, lhs); // TODO: Anonymous classes // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 if (classSymbol == null) return; String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); String methodName = JVMNames.getJVMMethodName(methodTree); AMethod method = clazz.methods.vivify(methodName); List params = methodTree.getParameters(); // Look-up parameter by name: for (int i = 0; i < params.size(); i++) { VariableTree vt = params.get(i); if (vt.getName().toString().equals(lhs.getName())) { Tree treeNode = rhs.getTree(); if (treeNode == null) { // TODO: Handle variable-length list as parameter. // An ArrayCreationNode with a null tree is created when the // parameter is a variable-length list. We are ignoring it for now. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 continue; } AnnotatedTypeMirror paramATM = atf.getAnnotatedType(vt); AnnotatedTypeMirror argATM = atf.getAnnotatedType(treeNode); AField param = method.parameters.vivify(i); helper.updateAnnotationSetInScene( param.type, atf, jaifPath, argATM, paramATM, TypeUseLocation.PARAMETER); break; } } } /** * Updates the receiver type of the method {@code methodElt} in the Scene * of the method's enclosing class based on the overridden method * {@code overriddenMethod} receiver type. *

* For the receiver in methodElt: *

    *
  • If the Scene does not contain an annotated type for the * receiver, then the type of the receiver on the overridden method will * be added to the receiver in the Scene.
  • *
  • If the Scene previously contained an annotated type for the * receiver, then its new type will be the LUB between the previous * type and the type of the receiver on the overridden method.
  • *
*

* @param methodTree the tree of the method that contains the receiver. * @param methodElt the element of the method. * @param overriddenMethod the overridden method. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the receiver type. */ @Override public void updateInferredMethodReceiverType( MethodTree methodTree, ExecutableElement methodElt, AnnotatedExecutableType overriddenMethod, AnnotatedTypeFactory atf) { ClassSymbol classSymbol = getEnclosingClassSymbol(methodTree); String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); String methodName = JVMNames.getJVMMethodName(methodElt); AMethod method = clazz.methods.vivify(methodName); AnnotatedDeclaredType argADT = overriddenMethod.getReceiverType(); if (argADT != null) { AnnotatedTypeMirror paramATM = atf.getAnnotatedType(methodTree).getReceiverType(); if (paramATM != null) { AField receiver = method.receiver; helper.updateAnnotationSetInScene( receiver.type, atf, jaifPath, argADT, paramATM, TypeUseLocation.RECEIVER); } } } /** * Updates the type of the field lhs in the Scene of the class with * tree classTree. If the field has a declaration annotation with the * {@link IgnoreInWholeProgramInference} meta-annotation, no type annotation * will be inferred for that field. *

* If the Scene contains no entry for the field lhs, * the entry will be created and its type will be the type of rhs. If the * Scene previously contained an entry/type for lhs, its new type will be * the LUB between the previous type and the type of rhs. *

* @param lhs the field whose type will be refined. * @param rhs the expression being assigned to the field. * @param classTree the ClassTree for the enclosing class of the assignment. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the field's type. */ @Override public void updateInferredFieldType( FieldAccessNode lhs, Node rhs, ClassTree classTree, AnnotatedTypeFactory atf) { ClassSymbol classSymbol = getEnclosingClassSymbol(classTree, lhs); // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 if (classSymbol == null) return; // TODO: Handle anonymous classes. // TODO: We must handle cases where the field is declared on a superclass. // Currently we are ignoring them. See ElementUtils#getSuperTypes. // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 if (!classSymbol.getEnclosedElements().contains(lhs.getElement())) return; // If the inferred field has a declaration annotation with the // @IgnoreInWholeProgramInference meta-annotation, exit this routine. for (AnnotationMirror declAnno : atf.getDeclAnnotations( InternalUtils.symbol(lhs.getTree()))) { Element elt = declAnno.getAnnotationType().asElement(); if (elt.getAnnotation(IgnoreInWholeProgramInference.class) != null) { return; } } String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); AField field = clazz.fields.vivify(lhs.getFieldName()); AnnotatedTypeMirror lhsATM = atf.getAnnotatedType(lhs.getTree()); AnnotatedTypeMirror rhsATM = atf.getAnnotatedType(rhs.getTree()); helper.updateAnnotationSetInScene( field.type, atf, jaifPath, rhsATM, lhsATM, TypeUseLocation.FIELD); } /** * Updates the return type of the method methodTree in the * Scene of the class with symbol classSymbol. *

* If the Scene does not contain an annotated return type for the method * methodTree, then the type of the value passed to the return expression * will be added to the return type of that method in the Scene. * If the Scene previously contained an annotated return type for the * method methodTree, its new type will be the LUB between the previous * type and the type of the value passed to the return expression. *

* @param retNode the node that contains the expression returned. * @param classSymbol the symbol of the class that contains the method. * @param methodTree the tree of the method whose return type * may be updated. * @param atf the annotated type factory of a given type system, whose * type hierarchy will be used to update the method's return type. */ @Override public void updateInferredMethodReturnType(ReturnNode retNode, ClassSymbol classSymbol, MethodTree methodTree, AnnotatedTypeFactory atf) { // See Issue 682 // https://github.com/typetools/checker-framework/issues/682 if (classSymbol == null) return; // TODO: Handle anonymous classes. String className = classSymbol.flatname.toString(); String jaifPath = helper.getJaifPath(className); AClass clazz = helper.getAClass(className, jaifPath); AMethod method = clazz.methods.vivify(JVMNames.getJVMMethodName(methodTree)); // Method return type AnnotatedTypeMirror lhsATM = atf.getAnnotatedType(methodTree).getReturnType(); // Type of the expression returned AnnotatedTypeMirror rhsATM = atf.getAnnotatedType(retNode.getTree().getExpression()); helper.updateAnnotationSetInScene( method.returnType, atf, jaifPath, rhsATM, lhsATM, TypeUseLocation.RETURN); } /** * Write all modified scenes into .jaif files. */ @Override public void saveResults() { helper.writeScenesToJaif(); } /** * Returns the ClassSymbol of the class encapsulating * the node n passed as parameter. *

* If the receiver of field is an instance of "this", the implementation * obtains the ClassSymbol by using classTree. Otherwise, the ClassSymbol * is from the field's receiver. */ // TODO: These methods below could be moved somewhere else. private ClassSymbol getEnclosingClassSymbol( ClassTree classTree, Node field) { Node receiverNode = null; if (field instanceof FieldAccessNode) { receiverNode = ((FieldAccessNode)field).getReceiver(); } else if (field instanceof LocalVariableNode) { receiverNode = ((LocalVariableNode)field).getReceiver(); } else { ErrorReporter.errorAbort("Unexpected type: " + field.getClass()); } if ((receiverNode == null || receiverNode instanceof ImplicitThisLiteralNode) && classTree != null) { return (ClassSymbol) InternalUtils.symbol(classTree); } TypeMirror type = receiverNode.getType(); if (type instanceof ClassType) { TypeSymbol tsym = ((ClassType) type).asElement(); return tsym.enclClass(); } return getEnclosingClassSymbol(receiverNode.getTree()); } /** * Returns the ClassSymbol of the class encapsulating * tree passed as parameter. */ private ClassSymbol getEnclosingClassSymbol(Tree tree) { Element symbol = InternalUtils.symbol(tree); if (symbol instanceof ClassSymbol) { return (ClassSymbol) symbol; } else if (symbol instanceof VarSymbol) { return ((VarSymbol)symbol).asType().asElement().enclClass(); } else if (symbol instanceof MethodSymbol) { return ((MethodSymbol)symbol).enclClass(); } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy