package edu.umd.cs.findbugs;
import java.util.List;
import java.util.Set;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.generic.GenericObjectType;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
public class DeepSubtypeAnalysis {
static private JavaClass serializable;
static private JavaClass collection;
static private JavaClass comparator;
static private JavaClass map;
static private JavaClass remote;
static private ClassNotFoundException storedException;
private static final boolean DEBUG = SystemProperties.getBoolean("dsa.debug");
static {
try {
serializable = AnalysisContext.lookupSystemClass("java.io.Serializable");
collection = AnalysisContext.lookupSystemClass("java.util.Collection");
map = AnalysisContext.lookupSystemClass("java.util.Map");
comparator = AnalysisContext.lookupSystemClass("java.util.Comparator");
} catch (ClassNotFoundException e) {
storedException = e;
}
try {
remote = AnalysisContext.lookupSystemClass("java.rmi.Remote");
} catch (ClassNotFoundException e) {
if (storedException == null) {
storedException = e;
}
}
}
public static double isDeepSerializable(ReferenceType type) throws ClassNotFoundException {
if (type instanceof ArrayType) {
ArrayType a = (ArrayType) type;
Type t = a.getBasicType();
if (t instanceof ReferenceType) {
type = (ReferenceType) t;
} else {
return 1.0;
}
}
double result = isDeepSerializable(type.getSignature());
if (type instanceof GenericObjectType && Subtypes2.isContainer(type)) {
GenericObjectType gt = (GenericObjectType) type;
List<? extends ReferenceType> parameters = gt.getParameters();
if (parameters != null) {
for(ReferenceType t : parameters) {
double r = isDeepSerializable(t);
if (result > r) {
result = r;
}
}
}
}
return result;
}
public static ReferenceType getLeastSerializableTypeComponent(ReferenceType type)
throws ClassNotFoundException {
if (type instanceof ArrayType) {
ArrayType a = (ArrayType) type;
Type t = a.getBasicType();
if (t instanceof ReferenceType) {
type = (ReferenceType) t;
} else {
return type;
}
}
ReferenceType result = type;
double value = isDeepSerializable(type.getSignature());
if (type instanceof GenericObjectType && Subtypes2.isContainer(type)) {
GenericObjectType gt = (GenericObjectType) type;
List<? extends ReferenceType> parameters = gt.getParameters();
if (parameters != null) {
for(ReferenceType t : parameters) {
double r = isDeepSerializable(t);
if (value > r) {
value = r;
result = getLeastSerializableTypeComponent(t);
}
}
}
}
return result;
}
public static double isDeepSerializable(@DottedClassName String refSig) throws ClassNotFoundException {
if (storedException != null) {
throw storedException;
}
if (isPrimitiveComponentClass(refSig)) {
if (DEBUG) {
System.out.println("regSig \"" + refSig + "\" is primitive component class");
}
return 1.0;
}
String refName = getComponentClass(refSig);
if ("java.lang.Object".equals(refName)) {
return 0.99;
}
JavaClass refJavaClass = Repository.lookupClass(refName);
return isDeepSerializable(refJavaClass);
}
public static double isDeepRemote(ReferenceType refType) {
return isDeepRemote(refType.getSignature());
}
public static double isDeepRemote(String refSig) {
if (remote == null) {
return 0.1;
}
String refName = getComponentClass(refSig);
if ("java.lang.Object".equals(refName)) {
return 0.99;
}
JavaClass refJavaClass;
try {
refJavaClass = Repository.lookupClass(refName);
return Analyze.deepInstanceOf(refJavaClass, remote);
} catch (ClassNotFoundException e) {
return 0.99;
}
}
private static boolean isPrimitiveComponentClass(String refSig) {
int c = 0;
while (c < refSig.length() && refSig.charAt(c) == '[') {
c++;
}
return c >= refSig.length() || refSig.charAt(c) != 'L';
}
public static String getComponentClass(ReferenceType refType) {
return getComponentClass(refType.getSignature());
}
public static String getComponentClass(String refSig) {
while (refSig.charAt(0) == '[') {
refSig = refSig.substring(1);
}
if (refSig.charAt(0) == 'L') {
return refSig.substring(1, refSig.length() - 1).replace('/', '.');
}
return refSig;
}
public static double isDeepSerializable(JavaClass x) throws ClassNotFoundException {
if (storedException != null) {
throw storedException;
}
if ("java.lang.Object".equals(x.getClassName())) {
return 0.4;
}
if (DEBUG) {
System.out.println("checking " + x.getClassName());
}
double result = Analyze.deepInstanceOf(x, serializable);
if (result >= 0.9) {
if (DEBUG) {
System.out.println("Direct high serializable result: " + result);
}
return result;
}
if (x.isFinal()) {
return result;
}
double collectionResult = Analyze.deepInstanceOf(x, collection);
double mapResult = Analyze.deepInstanceOf(x, map);
if (x.isInterface() || x.isAbstract()) {
result = Math.max(result, Math.max(mapResult, collectionResult) * 0.95);
if (result >= 0.9) {
return result;
}
}
ClassDescriptor classDescriptor = DescriptorFactory.createClassDescriptor(x);
Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(classDescriptor);
directSubtypes.remove(classDescriptor);
double confidence = 0.6;
if (x.isAbstract() || x.isInterface()) {
confidence = 0.8;
result = Math.max(result, 0.4);
} else if (directSubtypes.isEmpty()) {
confidence = 0.2;
}
double confidence2 = (1 + confidence) / 2;
result = Math.max(result, confidence2 * collectionResult);
if (result >= 0.9) {
if (DEBUG) {
System.out.println("High collection result: " + result);
}
return result;
}
result = Math.max(result, confidence2 * mapResult);
if (result >= 0.9) {
if (DEBUG) {
System.out.println("High map result: " + result);
}
return result;
}
result = Math.max(result, confidence2 * 0.5 * Analyze.deepInstanceOf(x, comparator));
if (result >= 0.9) {
if (DEBUG) {
System.out.println("High comparator result: " + result);
}
return result;
}
for (ClassDescriptor subtype : directSubtypes) {
JavaClass subJavaClass = Repository.lookupClass(subtype.getDottedClassName());
result = Math.max(result, confidence * Analyze.deepInstanceOf(subJavaClass, serializable));
if (result >= 0.9) {
return result;
}
}
if (DEBUG) {
System.out.println("No high results; max: " + result);
}
return result;
}
public static double deepInstanceOf(@DottedClassName String x, @DottedClassName String y) throws ClassNotFoundException {
return Analyze.deepInstanceOf(x, y);
}
public static double deepInstanceOf(JavaClass x, JavaClass y) throws ClassNotFoundException {
return Analyze.deepInstanceOf(x, y);
}
}