package codechicken.lib.asm;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
import org.objectweb.asm.tree.ClassNode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
* This is added as a class transformer if CodeChickenCore is installed. Adding it as a class transformer will speed evaluation up slightly by automatically caching superclasses when they are first loaded.
*/
public class ClassHeirachyManager implements IClassTransformer {
public static class SuperCache {
String superclass;
public HashSet<String> parents = new HashSet<String>();
private boolean flattened;
public void add(String parent) {
parents.add(parent);
}
public void flatten() {
if (flattened) {
return;
}
for (String s : new ArrayList<String>(parents)) {
SuperCache c = declareClass(s);
if (c != null) {
c.flatten();
parents.addAll(c.parents);
}
}
flattened = true;
}
}
public static HashMap<String, SuperCache> superclasses = new HashMap<String, SuperCache>();
private static LaunchClassLoader cl = Launch.classLoader;
public static String toKey(String name) {
if (ObfMapping.obfuscated) {
name = FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
}
return name;
}
public static String unKey(String name) {
if (ObfMapping.obfuscated) {
name = FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
}
return name;
}
/**
* @param name The class in question
* @param superclass The class being extended
* @return true if clazz extends, either directly or indirectly, superclass.
*/
public static boolean classExtends(String name, String superclass) {
name = toKey(name);
superclass = toKey(superclass);
if (name.equals(superclass)) {
return true;
}
SuperCache cache = declareClass(name);
if (cache == null)//just can't handle this
{
return false;
}
cache.flatten();
return cache.parents.contains(superclass);
}
private static SuperCache declareClass(String name) {
name = toKey(name);
SuperCache cache = superclasses.get(name);
if (cache != null) {
return cache;
}
try {
byte[] bytes = cl.getClassBytes(unKey(name));
if (bytes != null) {
cache = declareASM(bytes);
}
} catch (Exception e) {
}
if (cache != null) {
return cache;
}
try {
cache = declareReflection(name);
} catch (ClassNotFoundException e) {
}
return cache;
}
private static SuperCache declareReflection(String name) throws ClassNotFoundException {
Class<?> aclass = Class.forName(name);
SuperCache cache = getOrCreateCache(name);
if (aclass.isInterface()) {
cache.superclass = "java.lang.Object";
} else if (name.equals("java.lang.Object")) {
return cache;
} else {
cache.superclass = toKey(aclass.getSuperclass().getName());
}
cache.add(cache.superclass);
for (Class<?> iclass : aclass.getInterfaces()) {
cache.add(toKey(iclass.getName()));
}
return cache;
}
private static SuperCache declareASM(byte[] bytes) {
ClassNode node = ASMHelper.createClassNode(bytes);
String name = toKey(node.name);
SuperCache cache = getOrCreateCache(name);
cache.superclass = toKey(node.superName.replace('/', '.'));
cache.add(cache.superclass);
for (String iclass : node.interfaces) {
cache.add(toKey(iclass.replace('/', '.')));
}
return cache;
}
@Override
public byte[] transform(String name, String tname, byte[] bytes) {
if (bytes == null) {
return null;
}
if (!superclasses.containsKey(tname)) {
declareASM(bytes);
}
return bytes;
}
public static SuperCache getOrCreateCache(String name) {
SuperCache cache = superclasses.get(name);
if (cache == null) {
superclasses.put(name, cache = new SuperCache());
}
return cache;
}
public static String getSuperClass(String name, boolean runtime) {
name = toKey(name);
SuperCache cache = declareClass(name);
if (cache == null) {
return "java.lang.Object";
}
cache.flatten();
String s = cache.superclass;
if (!runtime) {
s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
}
return s;
}
}