Beispiel #1
0
  /**
   * Return the greatest lower bound of two types. That is, return the largest type that is a
   * subtype of both inputs. If none exists return {@code thisType}.
   */
  public JReferenceType strengthenType(JReferenceType thisType, JReferenceType thatType) {
    if (thisType == thatType) {
      return thisType;
    }

    if (thisType.isNullType() || thatType.isNullType()) {
      return JReferenceType.NULL_TYPE;
    }

    if (thisType.canBeNull() != thatType.canBeNull()) {
      // If either is non-nullable, the result should be non-nullable.
      return strengthenType(thisType.strengthenToNonNull(), thatType.strengthenToNonNull());
    }

    if (typeOracle.castSucceedsTrivially(thisType, thatType)) {
      return thisType;
    }

    if (typeOracle.castSucceedsTrivially(thatType, thisType)) {
      return thatType;
    }

    // This types are incompatible; ideally this code should not be reached, but there are two
    // situations where this happens:
    //   1 - unrelated interfaces;
    //   2 - unsafe code.
    // The original type is preserved in this case.
    return thisType;
  }
Beispiel #2
0
  /**
   * Return the greatest lower bound of two types. That is, return the largest type that is a
   * subtype of both inputs.
   */
  public JReferenceType strongerType(JReferenceType type1, JReferenceType type2) {
    if (type1 == type2) {
      return type1;
    }

    if (type1 instanceof JNullType || type2 instanceof JNullType) {
      return JNullType.INSTANCE;
    }

    if (type1 instanceof JNonNullType != type2 instanceof JNonNullType) {
      // If either is non-nullable, the result should be non-nullable.
      return strongerType(type1.getNonNull(), type2.getNonNull());
    }

    if (typeOracle.canTriviallyCast(type1, type2)) {
      return type1;
    }

    if (typeOracle.canTriviallyCast(type2, type1)) {
      return type2;
    }

    // cannot determine a strong type, just return the first one (this makes two
    // "unrelated" interfaces work correctly in TypeTightener
    return type1;
  }
Beispiel #3
0
  private JReferenceType generalizeArrayTypes(JArrayType thisArrayType, JArrayType thatArrayType) {
    assert thisArrayType != thatArrayType;

    int thisDims = thisArrayType.getDims();
    int thatDims = thatArrayType.getDims();

    int minDims = Math.min(thisDims, thatDims);
    /*
     * At a bare minimum, any two arrays generalize to an Object array with
     * one less dim than the lesser of the two; that is, int[][][][] and
     * String[][][] generalize to Object[][]. If minDims is 1, then they
     * just generalize to Object.
     */
    JReferenceType minimalGeneralType =
        (minDims == 1) ? typeJavaLangObject : getOrCreateArrayType(typeJavaLangObject, minDims - 1);

    if (thisDims == thatDims) {

      // Try to generalize by leaf types
      JType thisLeafType = thisArrayType.getLeafType();
      JType thatLeafType = thatArrayType.getLeafType();

      if (!(thisLeafType instanceof JReferenceType) || !(thatLeafType instanceof JReferenceType)) {
        return minimalGeneralType;
      }

      /*
       * Both are reference types; the result is the generalization of the leaf types combined with
       * the number of dims; that is, Foo[] and Bar[] generalize to X[] where X is the
       * generalization of Foo and Bar.
       *
       * Never generalize arrays to arrays of {@link JAnalysisDecoratedType}. One of the reasons is
       * that array initialization is not accounted for in {@link TypeTightener}.
       */
      JReferenceType leafGeneralization =
          generalizeTypes((JReferenceType) thisLeafType, (JReferenceType) thatLeafType)
              .getUnderlyingType();
      return getOrCreateArrayType(leafGeneralization, thisDims);
    }

    // Different number of dims
    if (typeOracle.castSucceedsTrivially(thatArrayType, thisArrayType)) {
      return thisArrayType;
    }

    if (typeOracle.castSucceedsTrivially(thisArrayType, thatArrayType)) {
      return thatArrayType;
    }

    // Totally unrelated
    return minimalGeneralType;
  }
Beispiel #4
0
  private JReferenceType generalizeInterfaces(
      JInterfaceType thisInterface, JInterfaceType thatInterface) {
    if (typeOracle.castSucceedsTrivially(thisInterface, thatInterface)) {
      return thatInterface;
    }

    if (typeOracle.castSucceedsTrivially(thatInterface, thisInterface)) {
      return thisInterface;
    }

    // unrelated
    return typeJavaLangObject;
  }
Beispiel #5
0
 public Collection<JType> getSubclasses(JType type) {
   return Collections2.transform(
       typeOracle.getSubTypeNames(type.getName()),
       new Function<String, JType>() {
         @Override
         public JType apply(String typeName) {
           return getFromTypeMap(typeName);
         }
       });
 }
Beispiel #6
0
  public EnumSet<DispatchType> getDispatchType(JReferenceType type) {
    if (!typeOracle.isInstantiatedType(type)) {
      return EnumSet.noneOf(DispatchType.class);
    }

    // Object methods can be dispatched to all four possible classes.
    if (type == getTypeJavaLangObject()) {
      return EnumSet.allOf(DispatchType.class);
    }

    EnumSet<DispatchType> dispatchSet = EnumSet.noneOf(DispatchType.class);
    DispatchType dispatchType = getRepresentedAsNativeTypesDispatchMap().get(type);
    if (dispatchType != null) {
      dispatchSet = EnumSet.of(dispatchType);
    } else if (typeOracle.isDualJsoInterface(type)) {
      // If it is an interface implemented both by JSOs and regular Java Objects;
      dispatchSet = EnumSet.of(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH, DispatchType.JSO);
    } else if (typeOracle.isSingleJsoImpl(type) || type.isJsoType()) {
      // If it is either an interface implemented by JSOs or JavaScriptObject or one of its
      // subclasses.
      dispatchSet = EnumSet.of(DispatchType.JSO);
    }

    for (JDeclaredType potentialNativeDispatchType : getRepresentedAsNativeTypes()) {
      if (potentialNativeDispatchType == type) {
        continue;
      }

      if (typeOracle.isInstantiatedType(potentialNativeDispatchType)
          && typeOracle.isSuperClassOrInterface(potentialNativeDispatchType, type)) {
        dispatchSet.add(getRepresentedAsNativeTypesDispatchMap().get(potentialNativeDispatchType));
        dispatchSet.add(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH);
      }
    }
    return dispatchSet;
  }
Beispiel #7
0
  private JReferenceType generalizeUnderlyingTypes(
      JReferenceType thisType, JReferenceType thatType) {

    // We should not have any analysis properties from this point forward.
    assert thisType == thisType.getUnderlyingType() && thatType == thatType.getUnderlyingType();

    if (thisType == thatType) {
      return thisType;
    }

    if (thisType instanceof JInterfaceType && thatType instanceof JInterfaceType) {
      return generalizeInterfaces((JInterfaceType) thisType, (JInterfaceType) thatType);
    }

    if (thisType instanceof JArrayType && thatType instanceof JArrayType) {
      return generalizeArrayTypes((JArrayType) thisType, (JArrayType) thatType);
    }

    if (thisType instanceof JClassType && thatType instanceof JClassType) {
      return generalizeClasses((JClassType) thisType, (JClassType) thatType);
    }

    JInterfaceType interfaceType =
        thisType instanceof JInterfaceType
            ? (JInterfaceType) thisType
            : (thatType instanceof JInterfaceType ? (JInterfaceType) thatType : null);

    JReferenceType nonInterfaceType = interfaceType == thisType ? thatType : thisType;

    // See if the class or the array is castable to the interface type.
    if (interfaceType != null
        && typeOracle.castSucceedsTrivially(nonInterfaceType, interfaceType)) {
      return interfaceType;
    }

    // unrelated: the best commonality is Object
    return typeJavaLangObject;
  }
Beispiel #8
0
 public boolean isJavaScriptObject(JType type) {
   if (type instanceof JReferenceType && typeSpecialJavaScriptObject != null) {
     return typeOracle.canTriviallyCast((JReferenceType) type, typeSpecialJavaScriptObject);
   }
   return false;
 }
Beispiel #9
0
  /**
   * Return the least upper bound of two types. That is, the smallest type that is a supertype of
   * both types.
   */
  public JReferenceType generalizeTypes(JReferenceType type1, JReferenceType type2) {
    if (type1 == type2) {
      return type1;
    }

    if (type1 instanceof JNonNullType && type2 instanceof JNonNullType) {
      // Neither can be null.
      type1 = type1.getUnderlyingType();
      type2 = type2.getUnderlyingType();
      return generalizeTypes(type1, type2).getNonNull();
    } else if (type1 instanceof JNonNullType) {
      // type2 can be null, so the result can be null
      type1 = type1.getUnderlyingType();
    } else if (type2 instanceof JNonNullType) {
      // type1 can be null, so the result can be null
      type2 = type2.getUnderlyingType();
    }
    assert !(type1 instanceof JNonNullType);
    assert !(type2 instanceof JNonNullType);

    int classify1 = classifyType(type1);
    int classify2 = classifyType(type2);

    if (classify1 == IS_NULL) {
      return type2;
    }

    if (classify2 == IS_NULL) {
      return type1;
    }

    if (classify1 == classify2) {

      // same basic kind of type
      if (classify1 == IS_INTERFACE) {

        if (typeOracle.canTriviallyCast(type1, type2)) {
          return type2;
        }

        if (typeOracle.canTriviallyCast(type2, type1)) {
          return type1;
        }

        // unrelated
        return typeJavaLangObject;

      } else if (classify1 == IS_ARRAY) {

        JArrayType aType1 = (JArrayType) type1;
        JArrayType aType2 = (JArrayType) type2;
        int dims1 = aType1.getDims();
        int dims2 = aType2.getDims();

        int minDims = Math.min(dims1, dims2);
        /*
         * At a bare minimum, any two arrays generalize to an Object array with
         * one less dim than the lesser of the two; that is, int[][][][] and
         * String[][][] generalize to Object[][]. If minDims is 1, then they
         * just generalize to Object.
         */
        JReferenceType minimalGeneralType;
        if (minDims > 1) {
          minimalGeneralType = getTypeArray(typeJavaLangObject, minDims - 1);
        } else {
          minimalGeneralType = typeJavaLangObject;
        }

        if (dims1 == dims2) {

          // Try to generalize by leaf types
          JType leafType1 = aType1.getLeafType();
          JType leafType2 = aType2.getLeafType();

          if (!(leafType1 instanceof JReferenceType) || !(leafType2 instanceof JReferenceType)) {
            return minimalGeneralType;
          }

          /*
           * Both are reference types; the result is the generalization of the
           * leaf types combined with the number of dims; that is, Foo[] and
           * Bar[] generalize to X[] where X is the generalization of Foo and
           * Bar.
           */
          JReferenceType leafRefType1 = (JReferenceType) leafType1;
          JReferenceType leafRefType2 = (JReferenceType) leafType2;

          /**
           * Never generalize arrays to arrays of {@link JNonNullType} as null array initialization
           * is not accounted for in {@link TypeTightener}.
           */
          JReferenceType leafGeneralization =
              generalizeTypes(leafRefType1, leafRefType2).getUnderlyingType();
          return getTypeArray(leafGeneralization, dims1);

        } else {

          // Conflicting number of dims

          // int[][] and Object[] generalize to Object[]
          JArrayType lesser = dims1 < dims2 ? aType1 : aType2;
          if (lesser.getLeafType() == typeJavaLangObject) {
            return lesser;
          }

          // Totally unrelated
          return minimalGeneralType;
        }

      } else {

        assert (classify1 == IS_CLASS);
        JClassType class1 = (JClassType) type1;
        JClassType class2 = (JClassType) type2;

        /*
         * see how far each type is from object; walk the one who's farther up
         * until they're even; then walk them up together until they meet (worst
         * case at Object)
         */
        int distance1 = countSuperTypes(class1);
        int distance2 = countSuperTypes(class2);
        for (; distance1 > distance2; --distance1) {
          class1 = class1.getSuperClass();
        }

        for (; distance1 < distance2; --distance2) {
          class2 = class2.getSuperClass();
        }

        while (class1 != class2) {
          class1 = class1.getSuperClass();
          class2 = class2.getSuperClass();
        }

        return class1;
      }
    } else {

      // different kinds of types
      int lesser = Math.min(classify1, classify2);
      int greater = Math.max(classify1, classify2);

      JReferenceType tLesser = classify1 < classify2 ? type1 : type2;
      JReferenceType tGreater = classify1 > classify2 ? type1 : type2;

      if (lesser == IS_INTERFACE && greater == IS_CLASS) {

        // just see if the class implements the interface
        if (typeOracle.canTriviallyCast(tGreater, tLesser)) {
          return tLesser;
        }

        // unrelated
        return typeJavaLangObject;

      } else if (greater == IS_ARRAY
          && ((tLesser == typeJavaLangCloneable) || (tLesser == typeJavaIoSerializable))) {
        return tLesser;
      } else {

        // unrelated: the best commonality between an interface and array, or
        // between an array and a class is Object
        return typeJavaLangObject;
      }
    }
  }