From af1041731c8c0ce1846ff7e7b6052ed7327a5dbc Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Feb 2015 22:23:45 -0500 Subject: fix translation issues, particularly with fields --- src/cuchaz/enigma/Deobfuscator.java | 25 +-- src/cuchaz/enigma/MainFormatConverter.java | 4 +- src/cuchaz/enigma/analysis/JarIndex.java | 57 +++---- .../analysis/MethodImplementationsTreeNode.java | 1 + .../enigma/analysis/RelatedMethodChecker.java | 96 +++++++++++ .../enigma/analysis/SourceIndexClassVisitor.java | 11 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 10 +- src/cuchaz/enigma/bytecode/ClassTranslator.java | 9 +- .../enigma/bytecode/MethodParameterWriter.java | 4 +- src/cuchaz/enigma/convert/ClassIdentity.java | 6 +- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/gui/GuiController.java | 9 +- .../enigma/mapping/BehaviorEntryFactory.java | 57 ------- src/cuchaz/enigma/mapping/EntryFactory.java | 184 +++++++++++++++++++++ src/cuchaz/enigma/mapping/JavassistUtil.java | 85 ---------- test/cuchaz/enigma/EntryFactory.java | 68 -------- test/cuchaz/enigma/TestEntryFactory.java | 68 ++++++++ .../enigma/TestJarIndexConstructorReferences.java | 2 +- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 2 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 5 +- test/cuchaz/enigma/TestTokensConstructors.java | 2 +- test/cuchaz/enigma/TestTranslator.java | 86 ++++++++-- test/cuchaz/enigma/TestType.java | 2 +- test/cuchaz/enigma/inputs/translation/A.java | 8 - test/cuchaz/enigma/inputs/translation/A_Basic.java | 22 +++ .../enigma/inputs/translation/B_BaseClass.java | 15 ++ .../enigma/inputs/translation/C_SubClass.java | 17 ++ test/cuchaz/enigma/resources/translation.mappings | 23 ++- 28 files changed, 574 insertions(+), 310 deletions(-) create mode 100644 src/cuchaz/enigma/analysis/RelatedMethodChecker.java delete mode 100644 src/cuchaz/enigma/mapping/BehaviorEntryFactory.java create mode 100644 src/cuchaz/enigma/mapping/EntryFactory.java delete mode 100644 src/cuchaz/enigma/mapping/JavassistUtil.java delete mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestEntryFactory.java delete mode 100644 test/cuchaz/enigma/inputs/translation/A.java create mode 100644 test/cuchaz/enigma/inputs/translation/A_Basic.java create mode 100644 test/cuchaz/enigma/inputs/translation/B_BaseClass.java create mode 100644 test/cuchaz/enigma/inputs/translation/C_SubClass.java diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b4ac501..b54a94a 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -42,16 +42,17 @@ import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.JarClassIterator; import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.RelatedMethodChecker; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.SourceIndexVisitor; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; @@ -115,25 +116,27 @@ public class Deobfuscator { } // drop mappings that don't match the jar + RelatedMethodChecker relatedMethodChecker = new RelatedMethodChecker(m_jarIndex); for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { - if (!checkClassMapping(classMapping)) { + if (!checkClassMapping(relatedMethodChecker, classMapping)) { val.removeClassMapping(classMapping); } } + // check for related method inconsistencies + if (relatedMethodChecker.hasProblems()) { + throw new Error("Related methods are inconsistent! Need to fix the mappings manually.\n" + relatedMethodChecker.getReport()); + } + m_mappings = val; m_renamer = new MappingsRenamer(m_jarIndex, val); m_translatorCache.clear(); } - private boolean checkClassMapping(ClassMapping classMapping) { + private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping) { // check the class - ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); - String outerClassName = m_jarIndex.getOuterClass(classEntry.getSimpleName()); - if (outerClassName != null) { - classEntry = new ClassEntry(outerClassName + "$" + classMapping.getObfName()); - } + ClassEntry classEntry = EntryFactory.getObfClassEntry(m_jarIndex, classMapping); if (!m_jarIndex.getObfClassEntries().contains(classEntry)) { return false; } @@ -149,16 +152,18 @@ public class Deobfuscator { // check methods for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - BehaviorEntry obfBehaviorEntry = BehaviorEntryFactory.createObf(classEntry, methodMapping); + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); classMapping.removeMethodMapping(methodMapping); } + + relatedMethodChecker.checkMethod(classEntry, methodMapping); } // check inner classes for (ClassMapping innerClassMapping : classMapping.innerClasses()) { - if (!checkClassMapping(innerClassMapping)) { + if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); classMapping.removeInnerClassMapping(innerClassMapping); } diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index 1848144..d4bb2db 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -18,7 +18,7 @@ import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.FieldMapping; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; @@ -35,7 +35,7 @@ public class MainFormatConverter { Map fieldTypes = Maps.newHashMap(); for (CtClass c : JarClassIterator.classes(jar)) { for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); fieldTypes.put(getFieldKey(fieldEntry), moveClasssesOutOfDefaultPackage(fieldEntry.getType())); } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index f54beda..24d110e 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -42,12 +42,11 @@ import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Translator; @@ -92,10 +91,10 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field)); + m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior)); + m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); } } @@ -166,7 +165,7 @@ public class JarIndex { private void indexBehavior(CtBehavior behavior) { // get the behavior entry - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); if (behaviorEntry instanceof MethodEntry) { MethodEntry methodEntry = (MethodEntry)behaviorEntry; @@ -178,12 +177,12 @@ public class JarIndex { private void indexBehaviorReferences(CtBehavior behavior) { // index method calls - final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); try { behavior.instrument(new ExprEditor() { @Override public void edit(MethodCall call) { - MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call); + MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { calledMethodEntry = new MethodEntry( @@ -202,7 +201,7 @@ public class JarIndex { @Override public void edit(FieldAccess call) { - FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call); + FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry); if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); @@ -217,7 +216,7 @@ public class JarIndex { @Override public void edit(ConstructorCall call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getMethodName(), @@ -228,7 +227,7 @@ public class JarIndex { @Override public void edit(NewExpr call) { - ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call); + ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); EntryReference reference = new EntryReference( calledConstructorEntry, call.getClassName(), @@ -256,7 +255,7 @@ public class JarIndex { } ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); // gather the classes from the illegally-set synthetic fields Set illegallySetClasses = Sets.newHashSet(); @@ -422,7 +421,7 @@ public class JarIndex { CtConstructor constructor = c.getDeclaredConstructors()[0]; // is this constructor called exactly once? - ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); Collection> references = getBehaviorReferences(constructorEntry); if (references.size() != 1) { return null; @@ -520,17 +519,17 @@ public class JarIndex { return rootNode; } - public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { + public List getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { - MethodEntry interfaceMethodEntry; + List interfaceMethodEntries = Lists.newArrayList(); // is this method on an interface? if (isInterface(obfMethodEntry.getClassName())) { - interfaceMethodEntry = obfMethodEntry; + interfaceMethodEntries.add(obfMethodEntry); } else { // get the interface class - List methodInterfaces = Lists.newArrayList(); for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { + // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( new ClassEntry(interfaceName), @@ -538,21 +537,20 @@ public class JarIndex { obfMethodEntry.getSignature() ); if (containsObfBehavior(methodInterface)) { - methodInterfaces.add(methodInterface); + interfaceMethodEntries.add(methodInterface); } } - if (methodInterfaces.isEmpty()) { - return null; - } - if (methodInterfaces.size() > 1) { - throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!"); - } - interfaceMethodEntry = methodInterfaces.get(0); } - MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); - rootNode.load(this); - return rootNode; + List nodes = Lists.newArrayList(); + if (!interfaceMethodEntries.isEmpty()) { + for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { + MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); + node.load(this); + nodes.add(node); + } + } + return nodes; } public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { @@ -569,9 +567,8 @@ public class JarIndex { } // look at interface methods too - MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry); - if (implementations != null) { - getRelatedMethodImplementations(methodEntries, implementations); + for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(null, methodEntry)) { + getRelatedMethodImplementations(methodEntries, implementationsNode); } // recurse diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 1009226..6cafc55 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -63,6 +63,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { } public void load(JarIndex index) { + // get all method implementations List nodes = Lists.newArrayList(); for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) { diff --git a/src/cuchaz/enigma/analysis/RelatedMethodChecker.java b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java new file mode 100644 index 0000000..5bd67a0 --- /dev/null +++ b/src/cuchaz/enigma/analysis/RelatedMethodChecker.java @@ -0,0 +1,96 @@ +package cuchaz.enigma.analysis; + +import java.util.Map; +import java.util.Set; + +import com.beust.jcommander.internal.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.MethodMapping; + +public class RelatedMethodChecker { + + private JarIndex m_jarIndex; + private Map,String> m_deobfNamesByGroup; + private Map m_deobfNamesByObfMethod; + private Map> m_groupsByObfMethod; + private Set> m_inconsistentGroups; + + public RelatedMethodChecker(JarIndex jarIndex) { + m_jarIndex = jarIndex; + m_deobfNamesByGroup = Maps.newHashMap(); + m_deobfNamesByObfMethod = Maps.newHashMap(); + m_groupsByObfMethod = Maps.newHashMap(); + m_inconsistentGroups = Sets.newHashSet(); + } + + public void checkMethod(ClassEntry classEntry, MethodMapping methodMapping) { + + // TEMP: disable the expensive check for now, maybe we can optimize it later, or just use it for debugging + if (true) return; + + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); + if (!(obfBehaviorEntry instanceof MethodEntry)) { + // only methods have related implementations + return; + } + MethodEntry obfMethodEntry = (MethodEntry)obfBehaviorEntry; + String deobfName = methodMapping.getDeobfName(); + m_deobfNamesByObfMethod.put(obfMethodEntry, deobfName); + + // have we seen this method's group before? + Set group = m_groupsByObfMethod.get(obfMethodEntry); + if (group == null) { + + // no, compute the group and save the name + group = m_jarIndex.getRelatedMethodImplementations(obfMethodEntry); + m_deobfNamesByGroup.put(group, deobfName); + + assert(group.contains(obfMethodEntry)); + for (MethodEntry relatedMethodEntry : group) { + m_groupsByObfMethod.put(relatedMethodEntry, group); + } + } + + // check the name + if (!sameName(m_deobfNamesByGroup.get(group), deobfName)) { + m_inconsistentGroups.add(group); + } + } + + private boolean sameName(String a, String b) { + if (a == null && b == null) { + return true; + } else if (a != null && b != null) { + return a.equals(b); + } + return false; + } + + public boolean hasProblems() { + return m_inconsistentGroups.size() > 0; + } + + public String getReport() { + StringBuilder buf = new StringBuilder(); + buf.append(m_inconsistentGroups.size()); + buf.append(" groups of methods related by inheritance and/or interfaces have different deobf names!\n"); + for (Set group : m_inconsistentGroups) { + buf.append("\tGroup with "); + buf.append(group.size()); + buf.append(" methods:\n"); + for (MethodEntry methodEntry : group) { + buf.append("\t\t"); + buf.append(methodEntry.toString()); + buf.append(" => "); + buf.append(m_deobfNamesByObfMethod.get(methodEntry)); + buf.append("\n"); + } + } + return buf.toString(); + } +} diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index e2ff300..d6692f6 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java @@ -26,11 +26,10 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.VariableInitializer; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Type; public class SourceIndexClassVisitor extends SourceIndexVisitor { @@ -69,12 +68,13 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature()); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(def); AstNode tokenNode = node.getNameToken(); + if (behaviorEntry instanceof ConstructorEntry) { ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry; if (constructorEntry.isStatic()) { + // for static initializers, check elsewhere for the token node tokenNode = node.getModifiers().firstOrNullObject(); } } @@ -85,8 +85,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); - ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); - ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature())); + ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(def); index.addDeclaration(node.getNameToken(), constructorEntry); return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); } diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index 7597c3a..8651ebd 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -37,7 +37,7 @@ 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.EntryFactory; import cuchaz.enigma.mapping.Translator; public class TranslationIndex implements Serializable { @@ -85,23 +85,23 @@ public class TranslationIndex implements Serializable { public void indexClass(CtClass c) { - ClassEntry classEntry = JavassistUtil.getClassEntry(c); + ClassEntry classEntry = EntryFactory.getClassEntry(c); // add the superclass - ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c); + ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { m_superclasses.put(classEntry, superclassEntry); } // add fields for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); } // add behaviors for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 4dba0d8..4167731 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -23,10 +23,9 @@ import javassist.bytecode.SourceFileAttribute; import com.google.common.collect.Maps; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; import cuchaz.enigma.mapping.MethodEntry; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; @@ -74,7 +73,7 @@ public class ClassTranslator { case ConstPool.CONST_InterfaceMethodref: { // translate the name and type - BehaviorEntry entry = BehaviorEntryFactory.create( + BehaviorEntry entry = EntryFactory.getBehaviorEntry( Descriptor.toJvmName(editor.getMemberrefClassname(i)), editor.getMemberrefName(i), editor.getMemberrefType(i) @@ -95,7 +94,7 @@ public class ClassTranslator { for (CtField field : c.getDeclaredFields()) { // translate the name - FieldEntry entry = JavassistUtil.getFieldEntry(field); + FieldEntry entry = EntryFactory.getFieldEntry(field); String translatedName = m_translator.translate(entry); if (translatedName != null) { field.setName(translatedName); @@ -112,7 +111,7 @@ public class ClassTranslator { CtMethod method = (CtMethod)behavior; // translate the name - MethodEntry entry = JavassistUtil.getMethodEntry(method); + MethodEntry entry = EntryFactory.getMethodEntry(method); String translatedName = m_translator.translate(entry); if (translatedName != null) { method.setName(translatedName); diff --git a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java index 853928c..f64ca02 100644 --- a/src/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/cuchaz/enigma/bytecode/MethodParameterWriter.java @@ -17,7 +17,7 @@ import javassist.CtBehavior; import javassist.CtClass; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.BehaviorEntryFactory; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Signature; import cuchaz.enigma.mapping.Translator; @@ -33,7 +33,7 @@ public class MethodParameterWriter { // Procyon will read method arguments from the "MethodParameters" attribute, so write those for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); // get the number of arguments Signature signature = behaviorEntry.getSignature(); diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index bb729a3..b514012 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -53,7 +53,7 @@ import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Signature; public class ClassIdentity { @@ -116,13 +116,13 @@ public class ClassIdentity { m_references = HashMultiset.create(); if (useReferences) { for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); for (EntryReference reference : index.getFieldReferences(fieldEntry)) { addReference(reference); } } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); for (EntryReference reference : index.getBehaviorReferences(behaviorEntry)) { addReference(reference); } diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index ccf6b78..d70b8eb 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -42,7 +42,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; -import cuchaz.enigma.mapping.JavassistUtil; +import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; @@ -242,13 +242,13 @@ public class ClassMatcher { System.err.println("\tAvailable dest methods:"); CtClass c = destLoader.loadClass(classMapping.getObfName()); for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } System.err.println("\tAvailable source methods:"); c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); for (CtBehavior behavior : c.getDeclaredBehaviors()) { - System.err.println("\t\t" + JavassistUtil.getBehaviorEntry(behavior)); + System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } } } diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 61fea9c..9fa633e 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -186,14 +186,17 @@ public class GuiController { public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); - MethodImplementationsTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodImplementations( + List rootNodes = m_deobfuscator.getJarIndex().getMethodImplementations( m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry ); - if (rootNode == null) { + if (rootNodes.isEmpty()) { return null; } - return MethodImplementationsTreeNode.findNode(rootNode, obfMethodEntry); + if (rootNodes.size() > 1) { + System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); + } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); } public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java deleted file mode 100644 index 61e501b..0000000 --- a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.mapping; - -import javassist.CtBehavior; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.bytecode.Descriptor; - -public class BehaviorEntryFactory { - - public static BehaviorEntry create(String className, String name, String signature) { - return create(new ClassEntry(className), name, signature); - } - - public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) { - if (name.equals("")) { - return new ConstructorEntry(classEntry, new Signature(signature)); - } else if (name.equals("")) { - return new ConstructorEntry(classEntry); - } else { - return new MethodEntry(classEntry, name, new Signature(signature)); - } - } - - public static BehaviorEntry create(CtBehavior behavior) { - String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName()); - if (behavior instanceof CtMethod) { - return create(className, behavior.getName(), behavior.getSignature()); - } else if (behavior instanceof CtConstructor) { - CtConstructor constructor = (CtConstructor)behavior; - if (constructor.isClassInitializer()) { - return create(className, "", null); - } else { - return create(className, "", constructor.getSignature()); - } - } else { - throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior); - } - } - - public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) { - return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature().toString()); - } - - public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) { - return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature().toString()); - } -} diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java new file mode 100644 index 0000000..dceea29 --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -0,0 +1,184 @@ +package cuchaz.enigma.mapping; + +import java.util.List; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.Descriptor; +import javassist.expr.ConstructorCall; +import javassist.expr.FieldAccess; +import javassist.expr.MethodCall; +import javassist.expr.NewExpr; + +import com.beust.jcommander.internal.Lists; +import com.strobel.assembler.metadata.MethodDefinition; + +import cuchaz.enigma.analysis.JarIndex; + +public class EntryFactory { + + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { + return new ClassEntry(getChainedOuterClassName(jarIndex, classMapping.getObfName())); + } + + private static String getChainedOuterClassName(JarIndex jarIndex, String obfClassName) { + + // lookup the chain of outer classes + List obfOuterClassNames = Lists.newArrayList(); + String checkName = obfClassName; + while (true) { + + // if this class name has a package, then it can't be an inner class + if (!new ClassEntry(checkName).isInDefaultPackage()) { + break; + } + + String obfOuterClassName = jarIndex.getOuterClass(checkName); + if (obfOuterClassName != null) { + obfOuterClassNames.add(obfOuterClassName); + checkName = obfOuterClassName; + } else { + break; + } + } + + // build the chained class name + StringBuilder buf = new StringBuilder(); + for (int i=obfOuterClassNames.size()-1; i>=0; i--) { + buf.append(obfOuterClassNames.get(i)); + buf.append("$"); + } + buf.append(obfClassName); + return buf.toString(); + } + + public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getDeobfName()); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry( + getClassEntry(field.getDeclaringClass()), + field.getName(), + new Type(field.getFieldInfo().getDescriptor()) + ); + } + + public static FieldEntry getFieldEntry(FieldAccess call) { + return new FieldEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getFieldName(), + new Type(call.getSignature()) + ); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry( + getClassEntry(method.getDeclaringClass()), + method.getName(), + new Signature(method.getMethodInfo().getDescriptor()) + ); + } + + public static MethodEntry getMethodEntry(MethodCall call) { + return new MethodEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + call.getMethodName(), + new Signature(call.getSignature()) + ); + } + + public static MethodEntry getMethodEntry(MethodDefinition def) { + return new MethodEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + def.getName(), + new Signature(def.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + if (constructor.isClassInitializer()) { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()) + ); + } else { + return new ConstructorEntry( + getClassEntry(constructor.getDeclaringClass()), + new Signature(constructor.getMethodInfo().getDescriptor()) + ); + } + } + + public static ConstructorEntry getConstructorEntry(ConstructorCall call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(NewExpr call) { + return new ConstructorEntry( + new ClassEntry(Descriptor.toJvmName(call.getClassName())), + new Signature(call.getSignature()) + ); + } + + public static ConstructorEntry getConstructorEntry(MethodDefinition def) { + if (def.isTypeInitializer()) { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()) + ); + } else { + return new ConstructorEntry( + new ClassEntry(def.getDeclaringType().getInternalName()), + new Signature(def.getSignature()) + ); + } + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod)behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor)behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { + return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); + } + + public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { + if (behaviorName.equals("")) { + return new ConstructorEntry(classEntry, behaviorSignature); + } else if(behaviorName.equals("")) { + return new ConstructorEntry(classEntry); + } else { + return new MethodEntry(classEntry, behaviorName, behaviorSignature); + } + } + + public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { + if (def.isConstructor() || def.isTypeInitializer()) { + return getConstructorEntry(def); + } else { + return getMethodEntry(def); + } + } + + public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { + return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); + } +} diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java deleted file mode 100644 index 0d6ce6a..0000000 --- a/src/cuchaz/enigma/mapping/JavassistUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -package cuchaz.enigma.mapping; - -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.Descriptor; -import javassist.expr.ConstructorCall; -import javassist.expr.FieldAccess; -import javassist.expr.MethodCall; -import javassist.expr.NewExpr; - -public class JavassistUtil { - - public static ClassEntry getClassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getName())); - } - - public static ClassEntry getSuperclassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); - } - - public static MethodEntry getMethodEntry(CtMethod method) { - return new MethodEntry( - getClassEntry(method.getDeclaringClass()), - method.getName(), - new Signature(method.getMethodInfo().getDescriptor()) - ); - } - - public static MethodEntry getMethodEntry(MethodCall call) { - return new MethodEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - call.getMethodName(), - new Signature(call.getSignature()) - ); - } - - public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { - return new ConstructorEntry( - getClassEntry(constructor.getDeclaringClass()), - new Signature(constructor.getMethodInfo().getDescriptor()) - ); - } - - public static ConstructorEntry getConstructorEntry(ConstructorCall call) { - return new ConstructorEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - new Signature(call.getSignature()) - ); - } - - public static ConstructorEntry getConstructorEntry(NewExpr call) { - return new ConstructorEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - new Signature(call.getSignature()) - ); - } - - public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { - if (behavior instanceof CtMethod) { - return getMethodEntry((CtMethod)behavior); - } else if (behavior instanceof CtConstructor) { - return getConstructorEntry((CtConstructor)behavior); - } - throw new Error("behavior is neither Method nor Constructor!"); - } - - public static FieldEntry getFieldEntry(CtField field) { - return new FieldEntry( - getClassEntry(field.getDeclaringClass()), - field.getName(), - new Type(field.getFieldInfo().getDescriptor()) - ); - } - - public static FieldEntry getFieldEntry(FieldAccess call) { - return new FieldEntry( - new ClassEntry(Descriptor.toJvmName(call.getClassName())), - call.getFieldName(), - new Type(call.getSignature()) - ); - } -} diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java deleted file mode 100644 index 8c94bdf..0000000 --- a/test/cuchaz/enigma/EntryFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin.\ - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma; - -import cuchaz.enigma.analysis.EntryReference; -import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ConstructorEntry; -import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.Signature; -import cuchaz.enigma.mapping.Type; - -public class EntryFactory { - - public static ClassEntry newClass(String name) { - return new ClassEntry(name); - } - - public static FieldEntry newField(String className, String fieldName, String fieldType) { - return newField(newClass(className), fieldName, fieldType); - } - - public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { - return new FieldEntry(classEntry, fieldName, new Type(fieldType)); - } - - public static MethodEntry newMethod(String className, String methodName, String methodSignature) { - return newMethod(newClass(className), methodName, methodSignature); - } - - public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { - return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); - } - - public static ConstructorEntry newConstructor(String className, String signature) { - return newConstructor(newClass(className), signature); - } - - public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { - return new ConstructorEntry(classEntry, new Signature(signature)); - } - - public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { - return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); - } - - public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { - return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); - } - - public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { - return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); - } - - public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { - return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); - } -} diff --git a/test/cuchaz/enigma/TestEntryFactory.java b/test/cuchaz/enigma/TestEntryFactory.java new file mode 100644 index 0000000..754f308 --- /dev/null +++ b/test/cuchaz/enigma/TestEntryFactory.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Signature; +import cuchaz.enigma.mapping.Type; + +public class TestEntryFactory { + + public static ClassEntry newClass(String name) { + return new ClassEntry(name); + } + + public static FieldEntry newField(String className, String fieldName, String fieldType) { + return newField(newClass(className), fieldName, fieldType); + } + + public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { + return new FieldEntry(classEntry, fieldName, new Type(fieldType)); + } + + public static MethodEntry newMethod(String className, String methodName, String methodSignature) { + return newMethod(newClass(className), methodName, methodSignature); + } + + public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { + return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); + } + + public static ConstructorEntry newConstructor(String className, String signature) { + return newConstructor(newClass(className), signature); + } + + public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { + return new ConstructorEntry(classEntry, new Signature(signature)); + } + + public static EntryReference newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { + return new EntryReference(fieldEntry, "", newConstructor(callerClassName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); + } + + public static EntryReference newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { + return new EntryReference(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); + } +} diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index 22812fe..e1a3022 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 349d33b..6e2c1ad 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index c0ac8d7..768284f 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -11,7 +11,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -96,8 +96,7 @@ public class TestJarIndexLoneClass { @Test public void methodImplementations() { MethodEntry source = newMethod("none/a", "a", "()Ljava/lang/String;"); - MethodImplementationsTreeNode node = m_index.getMethodImplementations(new Translator(), source); - assertThat(node, is(nullValue())); + assertThat(m_index.getMethodImplementations(new Translator(), source), is(empty())); } @Test diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java index 6758d2a..a563f83 100644 --- a/test/cuchaz/enigma/TestTokensConstructors.java +++ b/test/cuchaz/enigma/TestTokensConstructors.java @@ -10,7 +10,7 @@ ******************************************************************************/ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index f91c585..3fe1680 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java @@ -1,6 +1,6 @@ package cuchaz.enigma; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @@ -8,8 +8,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.jar.JarFile; +import org.junit.BeforeClass; import org.junit.Test; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.TranslationDirection; @@ -18,25 +20,85 @@ import cuchaz.enigma.mapping.Translator; public class TestTranslator { - private Deobfuscator m_deobfuscator; - private Mappings m_mappings; + private static Deobfuscator m_deobfuscator; + private static Mappings m_mappings; + private static Translator m_deobfTranslator; + private static Translator m_obfTranslator; - public TestTranslator() + @BeforeClass + public static void beforeClass() throws Exception { m_deobfuscator = new Deobfuscator(new JarFile("build/testTranslation.obf.jar")); - try (InputStream in = getClass().getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { + try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { m_mappings = new MappingsReader().read(new InputStreamReader(in)); m_deobfuscator.setMappings(m_mappings); + m_deobfTranslator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); + m_obfTranslator = m_deobfuscator.getTranslator(TranslationDirection.Obfuscating); } } @Test - public void deobfuscatingTranslations() - throws Exception { - Translator translator = m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating); - assertThat(translator.translateEntry(newClass("none/a")), is(newClass("deobf/A"))); - assertThat(translator.translateEntry(newField("none/a", "a", "I")), is(newField("deobf/A", "one", "I"))); - assertThat(translator.translateEntry(newField("none/a", "a", "F")), is(newField("deobf/A", "two", "F"))); - assertThat(translator.translateEntry(newField("none/a", "a", "Ljava/lang/String;")), is(newField("deobf/A", "three", "Ljava/lang/String;"))); + public void basicClasses() { + assertMapping(newClass("none/a"), newClass("deobf/A_Basic")); + assertMapping(newClass("none/b"), newClass("deobf/B_BaseClass")); + assertMapping(newClass("none/c"), newClass("deobf/C_SubClass")); + } + + @Test + public void basicFields() { + assertMapping(newField("none/a", "a", "I"), newField("deobf/A_Basic", "f1", "I")); + assertMapping(newField("none/a", "a", "F"), newField("deobf/A_Basic", "f2", "F")); + assertMapping(newField("none/a", "a", "Ljava/lang/String;"), newField("deobf/A_Basic", "f3", "Ljava/lang/String;")); + } + + @Test + public void basicMethods() { + assertMapping(newMethod("none/a", "a", "()V"), newMethod("deobf/A_Basic", "m1", "()V")); + assertMapping(newMethod("none/a", "a", "()I"), newMethod("deobf/A_Basic", "m2", "()I")); + assertMapping(newMethod("none/a", "a", "(I)V"), newMethod("deobf/A_Basic", "m3", "(I)V")); + assertMapping(newMethod("none/a", "a", "(I)I"), newMethod("deobf/A_Basic", "m4", "(I)I")); + } + + // TODO: basic constructors + + @Test + public void inheritanceFields() { + assertMapping(newField("none/b", "a", "I"), newField("deobf/B_BaseClass", "f1", "I")); + assertMapping(newField("none/b", "a", "C"), newField("deobf/B_BaseClass", "f2", "C")); + assertMapping(newField("none/c", "b", "I"), newField("deobf/C_SubClass", "f3", "I")); + assertMapping(newField("none/c", "c", "I"), newField("deobf/C_SubClass", "f4", "I")); + } + + @Test + public void inheritanceFieldsShadowing() { + assertMapping(newField("none/c", "b", "C"), newField("deobf/C_SubClass", "f2", "C")); + } + + @Test + public void inheritanceFieldsBySubClass() { + assertMapping(newField("none/c", "a", "I"), newField("deobf/C_SubClass", "f1", "I")); + // NOTE: can't reference b.C by subclass since it's shadowed + } + + @Test + public void inheritanceMethods() { + assertMapping(newMethod("none/b", "a", "()I"), newMethod("deobf/B_BaseClass", "m1", "()I")); + assertMapping(newMethod("none/b", "b", "()I"), newMethod("deobf/B_BaseClass", "m2", "()I")); + assertMapping(newMethod("none/c", "c", "()I"), newMethod("deobf/C_SubClass", "m3", "()I")); + } + + @Test + public void inheritanceMethodsOverrides() { + assertMapping(newMethod("none/c", "a", "()I"), newMethod("deobf/C_SubClass", "m1", "()I")); + } + + @Test + public void inheritanceMethodsBySubClass() { + assertMapping(newMethod("none/c", "b", "()I"), newMethod("deobf/C_SubClass", "m2", "()I")); + } + + private void assertMapping(Entry obf, Entry deobf) { + assertThat(m_deobfTranslator.translateEntry(obf), is(deobf)); + assertThat(m_obfTranslator.translateEntry(deobf), is(obf)); } } diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java index 7c3cebe..93f864b 100644 --- a/test/cuchaz/enigma/TestType.java +++ b/test/cuchaz/enigma/TestType.java @@ -7,7 +7,7 @@ import static org.hamcrest.Matchers.*; import cuchaz.enigma.mapping.Type; -import static cuchaz.enigma.EntryFactory.*; +import static cuchaz.enigma.TestEntryFactory.*; public class TestType { diff --git a/test/cuchaz/enigma/inputs/translation/A.java b/test/cuchaz/enigma/inputs/translation/A.java deleted file mode 100644 index b8aaf11..0000000 --- a/test/cuchaz/enigma/inputs/translation/A.java +++ /dev/null @@ -1,8 +0,0 @@ -package cuchaz.enigma.inputs.translation; - -public class A { - - public int one; - public float two; - public String three; -} diff --git a/test/cuchaz/enigma/inputs/translation/A_Basic.java b/test/cuchaz/enigma/inputs/translation/A_Basic.java new file mode 100644 index 0000000..8930763 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/A_Basic.java @@ -0,0 +1,22 @@ +package cuchaz.enigma.inputs.translation; + +public class A_Basic { + + public int one; + public float two; + public String three; + + public void m1() { + } + + public int m2() { + return 42; + } + + public void m3(int a1) { + } + + public int m4(int a1) { + return 5; // chosen by fair die roll, guaranteed to be random + } +} diff --git a/test/cuchaz/enigma/inputs/translation/B_BaseClass.java b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java new file mode 100644 index 0000000..44fbc8d --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/B_BaseClass.java @@ -0,0 +1,15 @@ +package cuchaz.enigma.inputs.translation; + +public class B_BaseClass { + + public int f1; + public char f2; + + public int m1() { + return 5; + } + + public int m2() { + return 42; + } +} diff --git a/test/cuchaz/enigma/inputs/translation/C_SubClass.java b/test/cuchaz/enigma/inputs/translation/C_SubClass.java new file mode 100644 index 0000000..8fe0b79 --- /dev/null +++ b/test/cuchaz/enigma/inputs/translation/C_SubClass.java @@ -0,0 +1,17 @@ +package cuchaz.enigma.inputs.translation; + +public class C_SubClass extends B_BaseClass { + + public char f2; // shadows B_BaseClass.f2 + public int f3; + public int f4; + + @Override + public int m1() { + return 32; + } + + public int m3() { + return 7; + } +} diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index c71493e..d87d437 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings @@ -1,4 +1,19 @@ -CLASS none/a deobf/A - FIELD a one I - FIELD a two F - FIELD a three Ljava/lang/String; \ No newline at end of file +CLASS none/a deobf/A_Basic + FIELD a f1 I + FIELD a f2 F + FIELD a f3 Ljava/lang/String; + METHOD a m1 ()V + METHOD a m2 ()I + METHOD a m3 (I)V + METHOD a m4 (I)I +CLASS none/b deobf/B_BaseClass + FIELD a f1 I + FIELD a f2 C + METHOD a m1 ()I + METHOD b m2 ()I +CLASS none/c deobf/C_SubClass + FIELD b f2 C + FIELD b f3 I + FIELD c f4 I + METHOD a m1 ()I + METHOD c m3 ()I -- cgit v1.2.3