package net.fybertech.dynamicmappings; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; public class InheritanceMap { //public static List<String> libraries = new ArrayList<String>(); public Map<String, InheritanceMap> mapCache = new HashMap<String, InheritanceMap>(); //public static Map<String, ClassNode> classCache = new HashMap<String, ClassNode>(); public List<String> privatefields = new ArrayList<String>(); public List<String> privatemethods = new ArrayList<String>(); public static class FieldHolder { public ClassNode cn; public FieldNode fn; public FieldHolder(ClassNode c, FieldNode f) { cn = c; fn = f; } } public static class MethodHolder { public ClassNode cn; public MethodNode mn; public MethodHolder(ClassNode c, MethodNode m) { cn = c; mn = m; } } public String className; public Map<String, HashSet<FieldHolder>> fields = new HashMap<String,HashSet<FieldHolder>>(); public Map<String, HashSet<MethodHolder>> methods = new HashMap<String,HashSet<MethodHolder>>(); public InheritanceMap() { } private InheritanceMap(ClassNode jc) { this.className = jc.name; for (FieldNode jf : jc.fields) { String fielddesc = jf.name + " " + jf.desc; if ((jf.access & Opcodes.ACC_PRIVATE) > 0) privatefields.add(fielddesc); HashSet<FieldHolder> fieldslist = this.fields.get(fielddesc); if (fieldslist == null) { fieldslist = new HashSet<FieldHolder>(); this.fields.put(fielddesc, fieldslist); } fieldslist.add(new FieldHolder(jc, jf)); } for (MethodNode jm : jc.methods) { String methoddesc = jm.name + " " + jm.desc; if ((jm.access & Opcodes.ACC_PRIVATE) > 0) privatemethods.add(methoddesc); HashSet<MethodHolder> methodslist = this.methods.get(methoddesc); if (methodslist == null) { methodslist = new HashSet<MethodHolder>(); this.methods.put(methoddesc, methodslist); } methodslist.add(new MethodHolder(jc, jm)); } } private void mergeMap(InheritanceMap cm) { for (String fielddesc : cm.fields.keySet()) { if (cm.privatefields.contains(fielddesc)) continue; if (this.fields.get(fielddesc) != null) this.fields.get(fielddesc).addAll(cm.fields.get(fielddesc)); else { HashSet<FieldHolder> f = new HashSet<FieldHolder>(); f.addAll(cm.fields.get(fielddesc)); this.fields.put(fielddesc, f); } } for (String methoddesc : cm.methods.keySet()) { if (cm.privatemethods.contains(methoddesc)) continue; if (this.methods.get(methoddesc) != null) this.methods.get(methoddesc).addAll(cm.methods.get(methoddesc)); else { HashSet<MethodHolder> m = new HashSet<MethodHolder>(); m.addAll(cm.methods.get(methoddesc)); this.methods.put(methoddesc, m); } } } public InheritanceMap buildMap(String paramClassname) throws IOException { ClassNode jc = locateClass(paramClassname); if (jc == null) return null; return buildMap(jc); } public InheritanceMap buildMap(ClassNode paramClass) throws IOException { InheritanceMap classmap = mapCache.get(paramClass.name); if (classmap != null) return classmap; //System.out.println("Begin buildMap: " + paramClass.getClassName()); classmap = new InheritanceMap(paramClass); for (String interfaceclassname : paramClass.interfaces) { ClassNode interfaceclass = locateClass(interfaceclassname); if (interfaceclass == null) { System.out.println("ERROR: Unable to locate " + interfaceclassname); continue; } classmap.mergeMap(buildMap(interfaceclass)); } if (paramClass.superName != null) { String superclassname = paramClass.superName; ClassNode superclass = locateClass(superclassname); if (superclass == null) System.out.println("ERROR: Unable to locate " + superclassname); else classmap.mergeMap(buildMap(superclass)); } //System.out.println("Finish buildMap: " + paramClass.getClassName()); mapCache.put(paramClass.name, classmap); return classmap; } public static ClassNode locateClassInJAR(String classname, String filename) throws IOException { JarFile jf = new JarFile(filename); for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) { JarEntry je = e.nextElement(); String name = je.getName(); if (!name.endsWith(".class")) continue; if (!classname.equals(name.replace(".class", ""))) continue; ClassReader reader = new ClassReader(jf.getInputStream(je)); ClassNode jc = new ClassNode(); reader.accept(jc, 0); if (jc.name.equals(classname)) { jf.close(); return jc; } } jf.close(); return null; } public ClassNode locateClass(String classname) throws IOException { return DynamicMappings.getClassNode(classname); /*ClassNode jc = InheritanceMap.classCache.get(classname); if (jc != null) return jc; for (String jarfile : InheritanceMap.libraries) { jc = locateClassInJAR(classname, jarfile); if (jc != null) { InheritanceMap.classCache.put(classname, jc); return jc; } } return null;*/ } /*public static void addSystemLibrary() { String systemJAR = System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar"; InheritanceMap.libraries.add(systemJAR); } public static void addLibrary(String paramLib) { InheritanceMap.libraries.add(paramLib); }*/ /*public static void addLibraryDir(String libDir) { List<String> jars = FyddleUtil.walkDirForExt(libDir, ".jar"); for (String jar : jars) InheritanceMap.addLibrary(jar); } public static void addClassPool(ClassPool classPool) { InheritanceMap.classCache.putAll(classPool.getPool()); }*/ //public static void reset() //{ //InheritanceMap.libraries.clear(); //InheritanceMap.classCache.clear(); //InheritanceMap.mapCache.clear(); //} // For debugging public static void main(String[] args) throws IOException { InheritanceMap base = new InheritanceMap(); InheritanceMap map = base.buildMap("ake"); System.out.println("Fields: "); for (String key : map.fields.keySet()) { System.out.println(" " + key); for (FieldHolder key2 : map.fields.get(key)) { System.out.println(" " + key2.cn.name); } } System.out.println("Methods: "); for (String key : map.methods.keySet()) { System.out.println(" " + key); for (MethodHolder key2 : map.methods.get(key)) { System.out.println(" " + key2.cn.name); } } } }