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

org.checkerframework.common.value.ValueCheckerUtils Maven / Gradle / Ivy

package org.checkerframework.common.value;

import com.sun.source.tree.Tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.common.value.util.NumberUtils;
import org.checkerframework.common.value.util.Range;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypeSystemError;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;

/** Utility methods for the Value Checker. */
public class ValueCheckerUtils {

  /** Do not instantiate. */
  private ValueCheckerUtils() {
    throw new TypeSystemError("do not instantiate");
  }

  /**
   * Get a list of the values of an annotation, and then cast the values to a given type.
   *
   * @param anno the annotation that contains values
   * @param castTo the type that is cast to
   * @param atypeFactory the type factory
   * @return a list of values after the casting
   */
  public static List getValuesCastedToType(
      AnnotationMirror anno, TypeMirror castTo, ValueAnnotatedTypeFactory atypeFactory) {
    return getValuesCastedToType(anno, castTo, false, atypeFactory);
  }

  /**
   * Get a list of the values of an annotation, and then cast the values to a given type.
   *
   * @param anno the annotation that contains values
   * @param castTo the unannotated type that is casted to
   * @param isUnsigned true if the type being casted to is unsigned
   * @param atypeFactory the type factory
   * @return a list of values after the casting
   */
  public static List getValuesCastedToType(
      AnnotationMirror anno,
      TypeMirror castTo,
      boolean isUnsigned,
      ValueAnnotatedTypeFactory atypeFactory) {
    Class castType = TypesUtils.getClassFromType(castTo);
    List values;
    switch (AnnotationUtils.annotationName(anno)) {
      case ValueAnnotatedTypeFactory.DOUBLEVAL_NAME:
        values = convertDoubleVal(anno, castType, castTo, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.INTVAL_NAME:
        List longs = atypeFactory.getIntValues(anno);
        values = convertIntVal(longs, castType, castTo, isUnsigned);
        break;
      case ValueAnnotatedTypeFactory.INTRANGE_NAME:
        Range range = atypeFactory.getRange(anno);
        List rangeValues = getValuesFromRange(range, Long.class);
        values = convertIntVal(rangeValues, castType, castTo, isUnsigned);
        break;
      case ValueAnnotatedTypeFactory.STRINGVAL_NAME:
        values = convertStringVal(anno, castType, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.BOOLVAL_NAME:
        values = convertBoolVal(anno, castType, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.BOTTOMVAL_NAME:
      case ValueAnnotatedTypeFactory.ARRAYLEN_NAME:
        values = Collections.emptyList();
        break;
      default:
        values = null;
    }
    return values;
  }

  /** Get the minimum and maximum of a list and return a range bounded by them. */
  public static @Nullable Range getRangeFromValues(List values) {
    if (values == null) {
      return null;
    } else if (values.isEmpty()) {
      return Range.NOTHING;
    }
    return Range.create(values);
  }

  /**
   * Converts a long value to a boxed numeric type.
   *
   * @param value a long value
   * @param expectedType the boxed numeric type of the result
   * @return {@code value} converted to {@code expectedType} using standard conversion rules
   */
  private static  T convertLongToType(long value, Class expectedType) {
    Object convertedValue;
    if (expectedType == Integer.class) {
      convertedValue = (int) value;
    } else if (expectedType == Short.class) {
      convertedValue = (short) value;
    } else if (expectedType == Byte.class) {
      convertedValue = (byte) value;
    } else if (expectedType == Long.class) {
      convertedValue = value;
    } else if (expectedType == Double.class) {
      convertedValue = (double) value;
    } else if (expectedType == Float.class) {
      convertedValue = (float) value;
    } else if (expectedType == Character.class) {
      convertedValue = (char) value;
    } else {
      throw new UnsupportedOperationException(
          "ValueCheckerUtils: unexpected class: " + expectedType);
    }
    return expectedType.cast(convertedValue);
  }

  /**
   * Get all possible values from the given type and cast them into a boxed primitive type. Returns
   * null if the list would have length greater than {@link ValueAnnotatedTypeFactory#MAX_VALUES}.
   *
   * 

{@code expectedType} must be a boxed type, not a primitive type, because primitive types * cannot be stored in a list. * * @param the type of the values to obtain * @param range the given range * @param expectedType the expected type * @return a list of all the values in the range, or null if there would be more than {@link * ValueAnnotatedTypeFactory#MAX_VALUES} */ public static @Nullable List getValuesFromRange( @Nullable Range range, Class expectedType) { if (range == null || range.isWiderThan(ValueAnnotatedTypeFactory.MAX_VALUES)) { return null; } if (range.isNothing()) { return Collections.emptyList(); } // The subtraction does not overflow, because the width has already been checked, so the // bound difference is less than ValueAnnotatedTypeFactory.MAX_VALUES. long boundDifference = range.to - range.from; // Each value is computed as a sum of the first value and an offset within the range, // to avoid having range.to as an upper bound of the loop. range.to can be Long.MAX_VALUE, // in which case a comparison value <= range.to would be always true. // boundDifference is always much smaller than Long.MAX_VALUE List values = new ArrayList<>((int) boundDifference + 1); for (long offset = 0; offset <= boundDifference; offset++) { long value = range.from + offset; values.add(convertLongToType(value, expectedType)); } return values; } /** * Converts a list of objects to a list of their string representations. * * @param origValues the objects to format * @return a list of the formatted objects */ private static @Nullable List convertToStringVal( List origValues) { if (origValues == null) { return null; } return CollectionsPlume.mapList(Object::toString, origValues); } /** * Convert the {@code value} argument/element of a @BoolVal annotation into a list. * * @param anno a @BoolVal annotation * @param newClass if String.class, the returned list is a {@code List} * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a @BoolVal annotation, as a {@code List} or a {@code * List} */ private static List convertBoolVal( AnnotationMirror anno, Class newClass, ValueAnnotatedTypeFactory atypeFactory) { List bools = AnnotationUtils.getElementValueArray(anno, atypeFactory.boolValValueElement, Boolean.class); if (newClass == String.class) { return convertToStringVal(bools); } return bools; } /** * Convert the {@code value} argument/element of a {@code @StringVal} annotation into a list. * * @param anno a {@code @StringVal} annotation * @param newClass if char[].class, the returned list is a {@code List} * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a {@code @StringVal} annotation, as a {@code List} or a * {@code List} */ private static List convertStringVal( AnnotationMirror anno, Class newClass, ValueAnnotatedTypeFactory atypeFactory) { List strings = atypeFactory.getStringValues(anno); if (newClass == char[].class) { return CollectionsPlume.mapList(String::toCharArray, strings); } return strings; } /** * Convert a list of longs to a given type * * @param longs the integral values to convert * @param newClass determines the type of the result * @param newType the type to which to cast, if newClass is numeric * @param isUnsigned if true, treat {@code newType} as unsigned * @return the {@code value} of a {@code @IntVal} annotation, as a {@code List} or a * {@code List} */ private static @Nullable List convertIntVal( List longs, Class newClass, TypeMirror newType, boolean isUnsigned) { if (longs == null) { return null; } if (newClass == String.class) { return convertToStringVal(longs); } else if (newClass == Character.class || newClass == char.class) { return CollectionsPlume.mapList((Long l) -> (char) l.longValue(), longs); } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert integral type to boolean"); } return NumberUtils.castNumbers(newType, isUnsigned, longs); } /** * Convert the {@code value} argument/element of a @StringVal annotation into a list. * * @param anno a {@code @DoubleVal} annotation * @param newClass the component type for the returned list * @param newType the component type for the returned list * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a {@code @DoubleVal} annotation */ private static @Nullable List convertDoubleVal( AnnotationMirror anno, Class newClass, TypeMirror newType, ValueAnnotatedTypeFactory atypeFactory) { List doubles = atypeFactory.getDoubleValues(anno); if (doubles == null) { return null; } if (newClass == String.class) { return convertToStringVal(doubles); } else if (newClass == Character.class || newClass == char.class) { return CollectionsPlume.mapList((Double l) -> (char) l.doubleValue(), doubles); } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert double to boolean"); } return NumberUtils.castNumbers(newType, doubles); } /** * Gets a list of lengths for a list of string values. * * @param values list of string values * @return list of unique lengths of strings in {@code values} */ public static List getLengthsForStringValues(List values) { List lengths = CollectionsPlume.mapList(String::length, values); return CollectionsPlume.withoutDuplicatesSorted(lengths); } /** * Returns a range representing the possible integral values represented by the passed {@code * AnnotatedTypeMirror}. If the passed {@code AnnotatedTypeMirror} does not contain an {@code * IntRange} annotation or an {@code IntVal} annotation, returns null. */ public static @Nullable Range getPossibleValues( AnnotatedTypeMirror valueType, ValueAnnotatedTypeFactory valueAnnotatedTypeFactory) { if (valueAnnotatedTypeFactory.isIntRange(valueType.getPrimaryAnnotations())) { return valueAnnotatedTypeFactory.getRange(valueType.getPrimaryAnnotation(IntRange.class)); } else { List values = valueAnnotatedTypeFactory.getIntValues(valueType.getPrimaryAnnotation(IntVal.class)); if (values != null) { return Range.create(values); } else { return null; } } } /** * Either returns the exact value of the given tree according to the Constant Value Checker, or * null if the exact value is not known. This method should only be used by clients who need * exactly one value -- such as the LBC's binary operator rules -- and not by those that need to * know whether a valueType belongs to a particular qualifier. */ public static @Nullable Long getExactValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null && possibleValues.from == possibleValues.to) { return possibleValues.from; } else { return null; } } /** * Returns the exact value of an annotated element according to the Constant Value Checker, or * null if the exact value is not known. * * @param element the element to get the exact value from * @param factory a ValueAnnotatedTypeFactory used for annotation accessing * @return the exact value of the element if it is constant, or null otherwise */ public static @Nullable Long getExactValue(Element element, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(element); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null && possibleValues.from == possibleValues.to) { return possibleValues.from; } else { return null; } } /** * Either returns the exact string value of the given tree according to the Constant Value * Checker, or null if the exact value is not known. This method should only be used by clients * who need exactly one value and not by those that need to know whether a valueType belongs to a * particular qualifier. */ public static @Nullable String getExactStringValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); if (valueType.hasPrimaryAnnotation(StringVal.class)) { AnnotationMirror valueAnno = valueType.getPrimaryAnnotation(StringVal.class); List possibleValues = AnnotationUtils.getElementValueArray( valueAnno, factory.stringValValueElement, String.class); if (possibleValues.size() == 1) { return possibleValues.get(0); } } return null; } /** * Finds the minimum value in a Value Checker type. If there is no information (such as when the * list of possible values is empty or null), returns null. Otherwise, returns the smallest value * in the list of possible values. */ public static @Nullable Long getMinValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null) { return possibleValues.from; } else { return null; } } /** * Finds the maximum value in a Value Checker type. If there is no information (such as when the * list of possible values is empty or null), returns null. Otherwise, returns the smallest value * in the list of possible values. */ public static @Nullable Long getMaxValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null) { return possibleValues.to; } else { return null; } } /** * Looks up the minlen of a member select tree. The tree must be an access to a sequence length. */ public static @Nullable Integer getMinLenFromTree(Tree tree, ValueAnnotatedTypeFactory valueATF) { AnnotatedTypeMirror minLenType = valueATF.getAnnotatedType(tree); Long min = valueATF.getMinimumIntegralValue(minLenType); if (min == null) { return null; } if (min < 0 || min > Integer.MAX_VALUE) { min = 0L; } return min.intValue(); } /** * Queries the Value Checker to determine if there is a known minimum length for the array * represented by {@code tree}. If not, returns 0. */ public static int getMinLen(Tree tree, ValueAnnotatedTypeFactory valueAnnotatedTypeFactory) { AnnotatedTypeMirror minLenType = valueAnnotatedTypeFactory.getAnnotatedType(tree); return valueAnnotatedTypeFactory.getMinLenValue(minLenType); } /** * Optimize the given JavaExpression. See {@link JavaExpressionOptimizer} for more details. * * @param je the expression to optimize * @param factory the annotated type factory * @return an optimized version of the argument */ public static JavaExpression optimize(JavaExpression je, AnnotatedTypeFactory factory) { ValueAnnotatedTypeFactory vatf = ((GenericAnnotatedTypeFactory) factory) .getTypeFactoryOfSubcheckerOrNull(ValueChecker.class); return new JavaExpressionOptimizer(vatf == null ? factory : vatf).convert(je); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy