public ReferenceValue generalize(TypedReferenceValue other) { // If both types are identical, the generalization is the same too. if (this.equals(other)) { return this; } String thisType = this.type; String otherType = other.type; // If both types are nul, the generalization is null too. if (thisType == null && otherType == null) { return ValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. if (thisType == null) { return other.generalizeMayBeNull(true); } // If the other type is null, the generalization is this type, maybe null. if (otherType == null) { return this.generalizeMayBeNull(true); } boolean mayBeNull = this.mayBeNull || other.mayBeNull; // If the two types are equal, the generalization remains the same, maybe null. if (thisType.equals(otherType)) { return typedReferenceValue(this, mayBeNull); } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); if (thisDimensionCount == otherDimensionCount) { // See if we can take into account the referenced classes. Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; if (thisReferencedClass != null && otherReferencedClass != null) { // Is one class simply an extension of the other one? if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return typedReferenceValue(other, mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return typedReferenceValue(this, mayBeNull); } // Do the classes have a non-trivial common superclass? Clazz commonClass = findCommonClass(thisReferencedClass, otherReferencedClass, false); if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) { // Otherwise, do the classes have a common interface? Clazz commonInterface = findCommonClass(thisReferencedClass, otherReferencedClass, true); if (commonInterface != null) { commonClass = commonInterface; } } return new TypedReferenceValue( commonDimensionCount == 0 ? commonClass.getName() : ClassUtil.internalArrayTypeFromClassName( commonClass.getName(), commonDimensionCount), commonClass, mayBeNull); } } else if (thisDimensionCount > otherDimensionCount) { // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(otherType))) { return typedReferenceValue(other, mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return typedReferenceValue(this, mayBeNull); } } // Reduce the common dimension count if either type is an array of // primitives type of this dimension. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) { commonDimensionCount--; } // Fall back on a basic Object or array of Objects type. return commonDimensionCount != 0 ? new TypedReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull) : mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; }
public int instanceOf(String otherType, Clazz otherReferencedClass) { String thisType = this.type; // If this type is null, it is never an instance of any class. if (thisType == null) { return NEVER; } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); // Strip any common array prefixes. thisType = thisType.substring(commonDimensionCount); otherType = otherType.substring(commonDimensionCount); // If either stripped type is a primitive type, we can tell right away. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) || ClassUtil.isInternalPrimitiveType(otherType.charAt(0)))) { return !thisType.equals(otherType) ? NEVER : mayBeNull ? MAYBE : ALWAYS; } // Strip the class type prefix and suffix of this type, if any. if (thisDimensionCount == commonDimensionCount) { thisType = ClassUtil.internalClassNameFromClassType(thisType); } // Strip the class type prefix and suffix of the other type, if any. if (otherDimensionCount == commonDimensionCount) { otherType = ClassUtil.internalClassNameFromClassType(otherType); } // If this type is an array type, and the other type is not // java.lang.Object, java.lang.Cloneable, or java.io.Serializable, // this type can never be an instance. if (thisDimensionCount > otherDimensionCount && !ClassUtil.isInternalArrayInterfaceName(otherType)) { return NEVER; } // If the other type is an array type, and this type is not // java.lang.Object, java.lang.Cloneable, or java.io.Serializable, // this type can never be an instance. if (thisDimensionCount < otherDimensionCount && !ClassUtil.isInternalArrayInterfaceName(thisType)) { return NEVER; } // If this type may be null, it might not be an instance of any class. if (mayBeNull) { return MAYBE; } // If this type is equal to the other type, or if the other type is // java.lang.Object, this type is always an instance. if (thisType.equals(otherType) || ClassConstants.NAME_JAVA_LANG_OBJECT.equals(otherType)) { return ALWAYS; } // If this type is an array type, it's ok. if (thisDimensionCount > otherDimensionCount) { return ALWAYS; } // If the other type is an array type, it might be ok. if (thisDimensionCount < otherDimensionCount) { return MAYBE; } // If the value extends the type, we're sure. return referencedClass != null && otherReferencedClass != null && referencedClass.extendsOrImplements(otherReferencedClass) ? ALWAYS : MAYBE; }
/** Returns the generalization of this ReferenceValue and the given other ReferenceValue. */ public ReferenceValue generalize(ReferenceValue other) { // If both types are identical, the generalization is the same too. if (this.equals(other)) { return this; } String thisType = this.type; String otherType = other.type; // If both types are nul, the generalization is null too. if (thisType == null && otherType == null) { return ValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. if (thisType == null) { return other.generalizeMayBeNull(true); } // If the other type is null, the generalization is this type, maybe null. if (otherType == null) { return this.generalizeMayBeNull(true); } boolean mayBeNull = this.mayBeNull || other.mayBeNull; // If the two types are equal, the generalization remains the same, maybe null. if (thisType.equals(otherType)) { return this.generalizeMayBeNull(mayBeNull); } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); if (thisDimensionCount == otherDimensionCount) { // See if we can take into account the referenced classes. Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; if (thisReferencedClass != null && otherReferencedClass != null) { if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return other.generalizeMayBeNull(mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return this.generalizeMayBeNull(mayBeNull); } // Collect the superclasses and interfaces of this class. Set thisSuperClasses = new HashSet(); thisReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(thisSuperClasses)); int thisSuperClassesCount = thisSuperClasses.size(); if (thisSuperClassesCount == 0 && thisReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + thisType + "] (not even immediate super class [" + thisReferencedClass.getSuperName() + "])"); } // Collect the superclasses and interfaces of the other class. Set otherSuperClasses = new HashSet(); otherReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(otherSuperClasses)); int otherSuperClassesCount = otherSuperClasses.size(); if (otherSuperClassesCount == 0 && otherReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + otherType + "] (not even immediate super class [" + otherReferencedClass.getSuperName() + "])"); } if (DEBUG) { System.out.println( "ReferenceValue.generalize this [" + thisReferencedClass.getName() + "] with other [" + otherReferencedClass.getName() + "]"); System.out.println(" This super classes: " + thisSuperClasses); System.out.println(" Other super classes: " + otherSuperClasses); } // Find the common superclasses. thisSuperClasses.retainAll(otherSuperClasses); if (DEBUG) { System.out.println(" Common super classes: " + thisSuperClasses); } // Find a class that is a subclass of all common superclasses, // or that at least has the maximum number of common superclasses. Clazz commonClass = null; int maximumSuperClassCount = -1; // Go over all common superclasses to find it. In case of // multiple subclasses, keep the lowest one alphabetically, // in order to ensure that the choice is deterministic. Iterator commonSuperClasses = thisSuperClasses.iterator(); while (commonSuperClasses.hasNext()) { Clazz commonSuperClass = (Clazz) commonSuperClasses.next(); int superClassCount = superClassCount(commonSuperClass, thisSuperClasses); if (maximumSuperClassCount < superClassCount || (maximumSuperClassCount == superClassCount && commonClass != null && commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) { commonClass = commonSuperClass; maximumSuperClassCount = superClassCount; } } if (commonClass == null) { throw new IllegalArgumentException( "Can't find common super class of [" + thisType + "] (with " + thisSuperClassesCount + " known super classes) and [" + otherType + "] (with " + otherSuperClassesCount + " known super classes)"); } if (DEBUG) { System.out.println(" Best common class: [" + commonClass.getName() + "]"); } // TODO: Handle more difficult cases, with multiple global subclasses. return new ReferenceValue( commonDimensionCount == 0 ? commonClass.getName() : ClassUtil.internalArrayTypeFromClassName( commonClass.getName(), commonDimensionCount), commonClass, mayBeNull); } } else if (thisDimensionCount > otherDimensionCount) { // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(otherType))) { return other.generalizeMayBeNull(mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return this.generalizeMayBeNull(mayBeNull); } } // Reduce the common dimension count if either type is an array of // primitives type of this dimension. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) { commonDimensionCount--; } // Fall back on a basic Object or array of Objects type. return commonDimensionCount == 0 ? mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL : new ReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull); }