Implement common superclass/interface algorithm in getCommonSupertype

The previous implementation simply returned java/lang/Object when given
two object types, which resulted in local object variables assigned in
more than one place being given a type of java/lang/Object. The
variables often had ugly casts wherever they were used.

The new implementation is from ASM's ClassWriter.
master
Graham 4 years ago
parent 34110df5f4
commit 0c1291b8ff
  1. 94
      src/org/jetbrains/java/decompiler/struct/gen/VarType.java

@ -2,8 +2,15 @@
package org.jetbrains.java.decompiler.struct.gen; package org.jetbrains.java.decompiler.struct.gen;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class VarType { // TODO: optimize switch public class VarType { // TODO: optimize switch
public static final VarType[] EMPTY_ARRAY = {}; public static final VarType[] EMPTY_ARRAY = {};
@ -30,6 +37,8 @@ public class VarType { // TODO: optimize switch
public static final VarType VARTYPE_SHORT_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Short"); public static final VarType VARTYPE_SHORT_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Short");
public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID); public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID);
private static final ClassLoader bootstrapClassLoader = new ClassLoader(null) {};
public final int type; public final int type;
public final int arrayDim; public final int arrayDim;
public final String value; public final String value;
@ -341,13 +350,96 @@ public class VarType { // TODO: optimize switch
return VARTYPE_INT; return VARTYPE_INT;
} }
case CodeConstants.TYPE_FAMILY_OBJECT: case CodeConstants.TYPE_FAMILY_OBJECT:
return VARTYPE_OBJECT; try {
String value = getCommonSuperClass(type1.value, type2.value);
return new VarType(CodeConstants.TYPE_OBJECT, 0, value);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
} }
} }
return null; return null;
} }
private static String getCommonSuperClass(String c, String d) throws ClassNotFoundException {
if (isAssignableFrom(c, d)) {
return c;
} else if (isAssignableFrom(d, c)) {
return d;
} else if (isInterface(c) || isInterface(d)) {
return "java/lang/Object";
}
do {
c = Objects.requireNonNull(getSuperClass(c));
} while (!isAssignableFrom(c, d));
return c;
}
private static boolean isAssignableFrom(String c, String d) throws ClassNotFoundException {
return c.equals(d) || isSuperClassOf(c, d) || isSuperInterfaceOf(c, d);
}
private static boolean isSuperClassOf(String c, String d) throws ClassNotFoundException {
String superClass = getSuperClass(d);
if (superClass != null) {
return c.equals(superClass) || isSuperClassOf(c, superClass);
} else {
return false;
}
}
private static boolean isSuperInterfaceOf(String c, String d) throws ClassNotFoundException {
for (String superInterface : getSuperInterfaces(d)) {
if (c.equals(superInterface) || isSuperInterfaceOf(c, superInterface)) {
return true;
}
}
return false;
}
private static boolean isInterface(String c) throws ClassNotFoundException {
StructClass structClass = DecompilerContext.getStructContext().getClass(c);
if (structClass != null) {
return structClass.hasModifier(CodeConstants.ACC_INTERFACE);
}
return bootstrapClassLoader.loadClass(c.replace('/', '.')).isInterface();
}
private static String getSuperClass(String c) throws ClassNotFoundException {
StructClass structClass = DecompilerContext.getStructContext().getClass(c);
if (structClass != null) {
if (structClass.superClass != null) {
return (String) structClass.superClass.value;
} else {
return null;
}
}
Class<?> superClass = bootstrapClassLoader.loadClass(c.replace('/', '.')).getSuperclass();
if (superClass != null) {
return superClass.getName().replace('.', '/');
} else {
return null;
}
}
private static List<String> getSuperInterfaces(String c) throws ClassNotFoundException {
StructClass structClass = DecompilerContext.getStructContext().getClass(c);
if (structClass != null) {
return Arrays.asList(structClass.getInterfaceNames());
}
Class<?>[] superInterfaces = bootstrapClassLoader.loadClass(c.replace('/', '.')).getInterfaces();
return Arrays.stream(superInterfaces)
.map(i -> i.getName().replace('.', '/'))
.collect(Collectors.toList());
}
public static VarType getMinTypeInFamily(int family) { public static VarType getMinTypeInFamily(int family) {
switch (family) { switch (family) {
case CodeConstants.TYPE_FAMILY_BOOLEAN: case CodeConstants.TYPE_FAMILY_BOOLEAN:

Loading…
Cancel
Save