/** * 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; }
/** * 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; }
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; }
private JReferenceType generalizeInterfaces( JInterfaceType thisInterface, JInterfaceType thatInterface) { if (typeOracle.castSucceedsTrivially(thisInterface, thatInterface)) { return thatInterface; } if (typeOracle.castSucceedsTrivially(thatInterface, thisInterface)) { return thisInterface; } // unrelated return typeJavaLangObject; }
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); } }); }
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; }
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; }
public boolean isJavaScriptObject(JType type) { if (type instanceof JReferenceType && typeSpecialJavaScriptObject != null) { return typeOracle.canTriviallyCast((JReferenceType) type, typeSpecialJavaScriptObject); } return false; }
/** * 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; } } }