From 2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 19 Jan 2015 22:22:57 -0500 Subject: solved tricky issue with incorrect translation of fields/methods referenced by a subclass instead of the declaring class --- src/cuchaz/enigma/analysis/TranslationIndex.java | 172 +++++++++++++++++------ 1 file changed, 130 insertions(+), 42 deletions(-) (limited to 'src/cuchaz/enigma/analysis/TranslationIndex.java') diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index c14fd59..4a356eb 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -11,97 +11,185 @@ package cuchaz.enigma.analysis; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; -import javassist.bytecode.Descriptor; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.Translator; + public class TranslationIndex implements Serializable { private static final long serialVersionUID = 738687982126844179L; - private Map m_superclasses; - private Multimap m_fields; + private Map m_superclasses; + private Multimap m_fieldEntries; + private Multimap m_behaviorEntries; public TranslationIndex() { m_superclasses = Maps.newHashMap(); - m_fields = HashMultimap.create(); - } - - public TranslationIndex(TranslationIndex other) { - m_superclasses = Maps.newHashMap(other.m_superclasses); - m_fields = HashMultimap.create(other.m_fields); + m_fieldEntries = HashMultimap.create(); + m_behaviorEntries = HashMultimap.create(); } - public void addSuperclass(String className, String superclassName) { - className = Descriptor.toJvmName(className); - superclassName = Descriptor.toJvmName(superclassName); + public TranslationIndex(TranslationIndex other, Translator translator) { - if (className.equals(superclassName)) { - throw new IllegalArgumentException("Class cannot be its own superclass! " + className); + // translate the superclasses + m_superclasses = Maps.newHashMap(); + for (Map.Entry mapEntry : other.m_superclasses.entrySet()) { + m_superclasses.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); } - if (!isJre(className) && !isJre(superclassName)) { - m_superclasses.put(className, superclassName); + // translate the fields + m_fieldEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_fieldEntries.entries()) { + m_fieldEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); + } + + m_behaviorEntries = HashMultimap.create(); + for (Map.Entry mapEntry : other.m_behaviorEntries.entries()) { + m_behaviorEntries.put( + translator.translateEntry(mapEntry.getKey()), + translator.translateEntry(mapEntry.getValue()) + ); } } - public void addField(String className, String fieldName) { - m_fields.put(className, fieldName); + public void indexClass(CtClass c) { + + ClassEntry classEntry = JavassistUtil.getClassEntry(c); + + // add the superclass + ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + if (!isJre(classEntry) && !isJre(superclassEntry)) { + m_superclasses.put(classEntry, superclassEntry); + } + + // add fields + for (CtField field : c.getDeclaredFields()) { + FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); + } + + // add behaviors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); + } } public void renameClasses(Map renames) { EntryRenamer.renameClassesInMap(renames, m_superclasses); - EntryRenamer.renameClassesInMultimap(renames, m_fields); + EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries); + EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries); } - public String getSuperclassName(String className) { - return m_superclasses.get(className); + public ClassEntry getSuperclass(ClassEntry classEntry) { + return m_superclasses.get(classEntry); } - public List getAncestry(String className) { - List ancestors = new ArrayList(); - while (className != null) { - className = getSuperclassName(className); - if (className != null) { - ancestors.add(className); + public List getAncestry(ClassEntry classEntry) { + List ancestors = Lists.newArrayList(); + while (classEntry != null) { + classEntry = getSuperclass(classEntry); + if (classEntry != null) { + ancestors.add(classEntry); } } return ancestors; } - public List getSubclassNames(String className) { + public List getSubclass(ClassEntry classEntry) { // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for (Map.Entry entry : m_superclasses.entrySet()) { - String subclass = entry.getKey(); - String superclass = entry.getValue(); - if (className.equals(superclass)) { + List subclasses = Lists.newArrayList(); + for (Map.Entry entry : m_superclasses.entrySet()) { + ClassEntry subclass = entry.getKey(); + ClassEntry superclass = entry.getValue(); + if (classEntry.equals(superclass)) { subclasses.add(subclass); } } return subclasses; } - public void getSubclassNamesRecursively(Set out, String className) { - for (String subclassName : getSubclassNames(className)) { - out.add(subclassName); - getSubclassNamesRecursively(out, subclassName); + public void getSubclassesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry); + getSubclassesRecursively(out, subclassEntry); + } + } + + public void getSubclassNamesRecursively(Set out, ClassEntry classEntry) { + for (ClassEntry subclassEntry : getSubclass(classEntry)) { + out.add(subclassEntry.getName()); + getSubclassNamesRecursively(out, subclassEntry); } } - public boolean containsField(String className, String fieldName) { - return m_fields.containsEntry(className, fieldName); + public boolean entryExists(Entry entry) { + if (entry instanceof FieldEntry) { + return fieldExists((FieldEntry)entry); + } else if (entry instanceof BehaviorEntry) { + return behaviorExists((BehaviorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry()); + } + throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); + } + + public boolean fieldExists(FieldEntry fieldEntry) { + return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); + } + + public boolean behaviorExists(BehaviorEntry behaviorEntry) { + return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); + } + + public ClassEntry resolveEntryClass(Entry entry) { + + if (entry instanceof ClassEntry) { + return (ClassEntry)entry; + } + + // this entry could refer to a method on a class where the method is not actually implemented + // travel up the inheritance tree to find the closest implementation + while (!entryExists(entry)) { + + // is there a parent class? + ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); + if (superclassEntry == null) { + // this is probably a method from a class in a library + // we can't trace the implementation up any higher unless we index the library + return null; + } + + // move up to the parent class + entry = entry.cloneToNewClass(superclassEntry); + } + return entry.getClassEntry(); } - private boolean isJre(String className) { - return className.startsWith("java/") || className.startsWith("javax/"); + private boolean isJre(ClassEntry classEntry) { + return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax"); } } -- cgit v1.2.3