diff options
| author | 2015-03-16 19:22:22 -0400 | |
|---|---|---|
| committer | 2015-03-16 19:22:22 -0400 | |
| commit | 5e3743a0aca3529eacf9be400c8b8d7547f66e7f (patch) | |
| tree | ea601747547f78e1b83ab828650932126440e221 | |
| parent | update to new javassist version to (hopefully) get bug fixes (diff) | |
| download | enigma-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.gz enigma-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.xz enigma-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.zip | |
started adding minimal support for generics
fixed mark-as-deobfuscated issue
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 9 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 31 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 7 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassRenamer.java | 164 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassTranslator.java | 42 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/EntryFactory.java | 17 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsChecker.java | 2 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsRenamer.java | 10 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ParameterizedType.java | 54 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Signature.java | 10 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Type.java | 78 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestSourceIndex.java | 2 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestTranslator.java | 17 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestType.java | 163 | ||||
| -rw-r--r-- | test/cuchaz/enigma/resources/translation.mappings | 11 |
15 files changed, 478 insertions, 139 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5a23ce5b..b63f1639 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -439,12 +439,9 @@ public class Deobfuscator { | |||
| 439 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); | 439 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); |
| 440 | if (obfEntry instanceof ClassEntry) { | 440 | if (obfEntry instanceof ClassEntry) { |
| 441 | ClassEntry obfClass = (ClassEntry)obfEntry; | 441 | ClassEntry obfClass = (ClassEntry)obfEntry; |
| 442 | ClassEntry translated = translator.translateEntry(obfClass); | 442 | List<ClassMapping> mappingChain = m_mappings.getClassMappingChain(obfClass); |
| 443 | if (obfClass.isInnerClass()) { | 443 | ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); |
| 444 | return !obfClass.getInnermostClassName().equals(translated.getInnermostClassName()); | 444 | return classMapping != null && classMapping.getDeobfName() != null; |
| 445 | } else { | ||
| 446 | return !obfClass.equals(translated); | ||
| 447 | } | ||
| 448 | } else if (obfEntry instanceof FieldEntry) { | 445 | } else if (obfEntry instanceof FieldEntry) { |
| 449 | return translator.translate((FieldEntry)obfEntry) != null; | 446 | return translator.translate((FieldEntry)obfEntry) != null; |
| 450 | } else if (obfEntry instanceof MethodEntry) { | 447 | } else if (obfEntry instanceof MethodEntry) { |
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd974..e255468a 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -28,6 +28,7 @@ import javassist.CtMethod; | |||
| 28 | import javassist.NotFoundException; | 28 | import javassist.NotFoundException; |
| 29 | import javassist.bytecode.AccessFlag; | 29 | import javassist.bytecode.AccessFlag; |
| 30 | import javassist.bytecode.Descriptor; | 30 | import javassist.bytecode.Descriptor; |
| 31 | import javassist.bytecode.EnclosingMethodAttribute; | ||
| 31 | import javassist.bytecode.FieldInfo; | 32 | import javassist.bytecode.FieldInfo; |
| 32 | import javassist.bytecode.InnerClassesAttribute; | 33 | import javassist.bytecode.InnerClassesAttribute; |
| 33 | import javassist.expr.ConstructorCall; | 34 | import javassist.expr.ConstructorCall; |
| @@ -314,15 +315,6 @@ public class JarIndex { | |||
| 314 | if (classEntry.isInnerClass()) { | 315 | if (classEntry.isInnerClass()) { |
| 315 | return classEntry.getOuterClassEntry(); | 316 | return classEntry.getOuterClassEntry(); |
| 316 | } | 317 | } |
| 317 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 318 | if (innerClassesAttribute != null) { | ||
| 319 | for (int i=0; i<innerClassesAttribute.tableLength(); i++) { | ||
| 320 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.innerClass(i))); | ||
| 321 | if (classEntry.equals(innerClassEntry)) { | ||
| 322 | return new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.outerClass(i))); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | ||
| 326 | 318 | ||
| 327 | // inner classes: | 319 | // inner classes: |
| 328 | // have constructors that can (illegally) set synthetic fields | 320 | // have constructors that can (illegally) set synthetic fields |
| @@ -481,6 +473,27 @@ public class JarIndex { | |||
| 481 | 473 | ||
| 482 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { | 474 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { |
| 483 | 475 | ||
| 476 | // is this class already marked anonymous? | ||
| 477 | EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute)c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); | ||
| 478 | if (enclosingMethodAttribute != null) { | ||
| 479 | if (enclosingMethodAttribute.methodIndex() > 0) { | ||
| 480 | return EntryFactory.getBehaviorEntry( | ||
| 481 | Descriptor.toJvmName(enclosingMethodAttribute.className()), | ||
| 482 | enclosingMethodAttribute.methodName(), | ||
| 483 | enclosingMethodAttribute.methodDescriptor() | ||
| 484 | ); | ||
| 485 | } else { | ||
| 486 | // an attribute but no method? assume not anonymous | ||
| 487 | return null; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous | ||
| 492 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 493 | if (innerClassesAttribute != null) { | ||
| 494 | return null; | ||
| 495 | } | ||
| 496 | |||
| 484 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 497 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 485 | 498 | ||
| 486 | // anonymous classes: | 499 | // anonymous classes: |
diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4f49568..f4202b5b 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | |||
| @@ -30,7 +30,6 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 30 | import cuchaz.enigma.mapping.ConstructorEntry; | 30 | import cuchaz.enigma.mapping.ConstructorEntry; |
| 31 | import cuchaz.enigma.mapping.EntryFactory; | 31 | import cuchaz.enigma.mapping.EntryFactory; |
| 32 | import cuchaz.enigma.mapping.FieldEntry; | 32 | import cuchaz.enigma.mapping.FieldEntry; |
| 33 | import cuchaz.enigma.mapping.Type; | ||
| 34 | 33 | ||
| 35 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 34 | public class SourceIndexClassVisitor extends SourceIndexVisitor { |
| 36 | 35 | ||
| @@ -93,8 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 93 | @Override | 92 | @Override |
| 94 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { | 93 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { |
| 95 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 94 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 96 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); | 95 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); |
| 97 | FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); | ||
| 98 | assert (node.getVariables().size() == 1); | 96 | assert (node.getVariables().size() == 1); |
| 99 | VariableInitializer variable = node.getVariables().firstOrNullObject(); | 97 | VariableInitializer variable = node.getVariables().firstOrNullObject(); |
| 100 | index.addDeclaration(variable.getNameToken(), fieldEntry); | 98 | index.addDeclaration(variable.getNameToken(), fieldEntry); |
| @@ -106,8 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 106 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { | 104 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { |
| 107 | // treat enum declarations as field declarations | 105 | // treat enum declarations as field declarations |
| 108 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 106 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 109 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); | 107 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); |
| 110 | FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); | ||
| 111 | index.addDeclaration(node.getNameToken(), fieldEntry); | 108 | index.addDeclaration(node.getNameToken(), fieldEntry); |
| 112 | 109 | ||
| 113 | return recurse(node, index); | 110 | return recurse(node, index); |
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index e9cdea3c..8bc084d3 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java | |||
| @@ -23,60 +23,100 @@ import com.google.common.collect.Maps; | |||
| 23 | import com.google.common.collect.Sets; | 23 | import com.google.common.collect.Sets; |
| 24 | 24 | ||
| 25 | import cuchaz.enigma.mapping.ClassEntry; | 25 | import cuchaz.enigma.mapping.ClassEntry; |
| 26 | import cuchaz.enigma.mapping.ClassNameReplacer; | ||
| 27 | import cuchaz.enigma.mapping.ParameterizedType; | ||
| 28 | import cuchaz.enigma.mapping.Translator; | ||
| 29 | import cuchaz.enigma.mapping.Type; | ||
| 26 | 30 | ||
| 27 | public class ClassRenamer { | 31 | public class ClassRenamer { |
| 28 | 32 | ||
| 29 | public static void renameClasses(CtClass c, Map<ClassEntry,ClassEntry> map) { | 33 | public static void renameClasses(CtClass c, final Translator translator) { |
| 30 | 34 | renameClasses(c, new ClassNameReplacer() { | |
| 31 | // build the map used by javassist | 35 | @Override |
| 32 | ClassMap nameMap = new ClassMap(); | 36 | public String replace(String className) { |
| 33 | for (Map.Entry<ClassEntry,ClassEntry> entry : map.entrySet()) { | 37 | ClassEntry entry = translator.translateEntry(new ClassEntry(className)); |
| 34 | nameMap.put(entry.getKey().getName(), entry.getValue().getName()); | 38 | if (entry != null) { |
| 35 | } | 39 | return entry.getName(); |
| 36 | |||
| 37 | c.replaceClassName(nameMap); | ||
| 38 | |||
| 39 | // replace simple names in the InnerClasses attribute too | ||
| 40 | ConstPool constants = c.getClassFile().getConstPool(); | ||
| 41 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 42 | if (attr != null) { | ||
| 43 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 44 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); | ||
| 45 | if (attr.innerNameIndex(i) != 0) { | ||
| 46 | attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 47 | } | 40 | } |
| 48 | 41 | return null; | |
| 49 | /* DEBUG | ||
| 50 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | ||
| 51 | */ | ||
| 52 | } | 42 | } |
| 53 | } | 43 | }); |
| 54 | } | 44 | } |
| 55 | 45 | ||
| 56 | public static Set<ClassEntry> getAllClassEntries(final CtClass c) { | 46 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { |
| 47 | renameClasses(c, new ClassNameReplacer() { | ||
| 48 | @Override | ||
| 49 | public String replace(String className) { | ||
| 50 | ClassEntry entry = new ClassEntry(className); | ||
| 51 | if (entry.isInDefaultPackage()) { | ||
| 52 | return newPackageName + "/" + entry.getName(); | ||
| 53 | } | ||
| 54 | return null; | ||
| 55 | } | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | |||
| 59 | public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { | ||
| 60 | renameClasses(c, new ClassNameReplacer() { | ||
| 61 | @Override | ||
| 62 | public String replace(String className) { | ||
| 63 | ClassEntry entry = new ClassEntry(className); | ||
| 64 | if (entry.getPackageName().equals(oldPackageName)) { | ||
| 65 | return entry.getSimpleName(); | ||
| 66 | } | ||
| 67 | return null; | ||
| 68 | } | ||
| 69 | }); | ||
| 70 | } | ||
| 71 | |||
| 72 | public static void renameClasses(CtClass c, ClassNameReplacer replacer) { | ||
| 73 | Map<ParameterizedType,ParameterizedType> map = Maps.newHashMap(); | ||
| 74 | for (ParameterizedType type : ClassRenamer.getAllClassTypes(c)) { | ||
| 75 | ParameterizedType renamedType = new ParameterizedType(type, replacer); | ||
| 76 | if (!type.equals(renamedType)) { | ||
| 77 | map.put(type, renamedType); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | renameTypes(c, map); | ||
| 81 | } | ||
| 82 | |||
| 83 | public static Set<ParameterizedType> getAllClassTypes(final CtClass c) { | ||
| 57 | 84 | ||
| 58 | // get the classes that javassist knows about | 85 | // TODO: might have to scan SignatureAttributes directly because javassist is buggy |
| 59 | final Set<ClassEntry> entries = Sets.newHashSet(); | 86 | |
| 87 | // get the class types that javassist knows about | ||
| 88 | final Set<ParameterizedType> types = Sets.newHashSet(); | ||
| 60 | ClassMap map = new ClassMap() { | 89 | ClassMap map = new ClassMap() { |
| 61 | @Override | 90 | @Override |
| 62 | public Object get(Object obj) { | 91 | public Object get(Object obj) { |
| 63 | if (obj instanceof String) { | 92 | if (obj instanceof String) { |
| 64 | String str = (String)obj; | 93 | String str = (String)obj; |
| 65 | 94 | ||
| 66 | // javassist throws a lot of weird things at this map | 95 | // sometimes javasist gives us dot-separated classes... whadda hell? |
| 67 | // I either have to implement my on class scanner, or just try to filter out the weirdness | 96 | str = str.replace('.', '/'); |
| 68 | // I'm opting to filter out the weirdness for now | ||
| 69 | 97 | ||
| 70 | // skip anything with generic arguments | 98 | // skip weird types |
| 71 | if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { | 99 | boolean hasNestedParams = str.indexOf('<') >= 0 && str.indexOf('<', str.indexOf('<')+1) >= 0; |
| 100 | boolean hasWeirdChars = str.indexOf('*') >= 0 || str.indexOf('-') >= 0 || str.indexOf('+') >= 0; | ||
| 101 | if (hasNestedParams || hasWeirdChars) { | ||
| 102 | // TEMP | ||
| 103 | System.out.println("Skipped translating: " + str); | ||
| 72 | return null; | 104 | return null; |
| 73 | } | 105 | } |
| 74 | 106 | ||
| 75 | // convert path/to/class.inner to path/to/class$inner | 107 | ParameterizedType type = new ParameterizedType(new Type("L" + str + ";")); |
| 76 | str = str.replace('.', '$'); | 108 | assert(type.isClass()); |
| 109 | // TEMP | ||
| 110 | try { | ||
| 111 | type.getClassEntry(); | ||
| 112 | } catch (Throwable t) { | ||
| 113 | // bad type | ||
| 114 | // TEMP | ||
| 115 | System.out.println("Skipped translating: " + str); | ||
| 116 | return null; | ||
| 117 | } | ||
| 77 | 118 | ||
| 78 | // remember everything else | 119 | types.add(type); |
| 79 | entries.add(new ClassEntry(str)); | ||
| 80 | } | 120 | } |
| 81 | return null; | 121 | return null; |
| 82 | } | 122 | } |
| @@ -85,26 +125,46 @@ public class ClassRenamer { | |||
| 85 | }; | 125 | }; |
| 86 | c.replaceClassName(map); | 126 | c.replaceClassName(map); |
| 87 | 127 | ||
| 88 | return entries; | 128 | return types; |
| 89 | } | 129 | } |
| 90 | 130 | ||
| 91 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { | 131 | public static void renameTypes(CtClass c, Map<ParameterizedType,ParameterizedType> map) { |
| 92 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 132 | |
| 93 | for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { | 133 | // convert the type map to a javassist class map |
| 94 | if (classEntry.isInDefaultPackage()) { | 134 | ClassMap nameMap = new ClassMap(); |
| 95 | map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); | 135 | for (Map.Entry<ParameterizedType,ParameterizedType> entry : map.entrySet()) { |
| 96 | } | 136 | String source = entry.getKey().toString(); |
| 137 | String dest = entry.getValue().toString(); | ||
| 138 | |||
| 139 | // don't forget to chop off the L ... ; | ||
| 140 | // javassist doesn't want it there | ||
| 141 | source = source.substring(1, source.length() - 1); | ||
| 142 | dest = dest.substring(1, dest.length() - 1); | ||
| 143 | |||
| 144 | nameMap.put(source, dest); | ||
| 97 | } | 145 | } |
| 98 | ClassRenamer.renameClasses(c, map); | 146 | |
| 99 | } | 147 | // replace!! |
| 100 | 148 | c.replaceClassName(nameMap); | |
| 101 | public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { | 149 | |
| 102 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 150 | // replace simple names in the InnerClasses attribute too |
| 103 | for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { | 151 | ConstPool constants = c.getClassFile().getConstPool(); |
| 104 | if (classEntry.getPackageName().equals(oldPackageName)) { | 152 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 105 | map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); | 153 | if (attr != null) { |
| 154 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 155 | |||
| 156 | // get the inner class full name (which has already been translated) | ||
| 157 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); | ||
| 158 | |||
| 159 | if (attr.innerNameIndex(i) != 0) { | ||
| 160 | // update the inner name | ||
| 161 | attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* DEBUG | ||
| 165 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | ||
| 166 | */ | ||
| 106 | } | 167 | } |
| 107 | } | 168 | } |
| 108 | ClassRenamer.renameClasses(c, map); | ||
| 109 | } | 169 | } |
| 110 | } | 170 | } |
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 94ab2c4b..7952577b 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java | |||
| @@ -10,8 +10,6 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode; | 11 | package cuchaz.enigma.bytecode; |
| 12 | 12 | ||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | import javassist.CtBehavior; | 13 | import javassist.CtBehavior; |
| 16 | import javassist.CtClass; | 14 | import javassist.CtClass; |
| 17 | import javassist.CtField; | 15 | import javassist.CtField; |
| @@ -19,9 +17,6 @@ import javassist.CtMethod; | |||
| 19 | import javassist.bytecode.ConstPool; | 17 | import javassist.bytecode.ConstPool; |
| 20 | import javassist.bytecode.Descriptor; | 18 | import javassist.bytecode.Descriptor; |
| 21 | import javassist.bytecode.SourceFileAttribute; | 19 | import javassist.bytecode.SourceFileAttribute; |
| 22 | |||
| 23 | import com.google.common.collect.Maps; | ||
| 24 | |||
| 25 | import cuchaz.enigma.mapping.BehaviorEntry; | 20 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 26 | import cuchaz.enigma.mapping.ClassEntry; | 21 | import cuchaz.enigma.mapping.ClassEntry; |
| 27 | import cuchaz.enigma.mapping.EntryFactory; | 22 | import cuchaz.enigma.mapping.EntryFactory; |
| @@ -50,20 +45,15 @@ public class ClassTranslator { | |||
| 50 | 45 | ||
| 51 | case ConstPool.CONST_Fieldref: { | 46 | case ConstPool.CONST_Fieldref: { |
| 52 | 47 | ||
| 53 | // translate the name | 48 | // translate the name and type |
| 54 | FieldEntry entry = new FieldEntry( | 49 | FieldEntry entry = EntryFactory.getFieldEntry( |
| 55 | new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), | 50 | Descriptor.toJvmName(constants.getFieldrefClassName(i)), |
| 56 | constants.getFieldrefName(i), | 51 | constants.getFieldrefName(i), |
| 57 | new Type(constants.getFieldrefType(i)) | 52 | constants.getFieldrefType(i) |
| 58 | ); | 53 | ); |
| 59 | FieldEntry translatedEntry = m_translator.translateEntry(entry); | 54 | FieldEntry translatedEntry = m_translator.translateEntry(entry); |
| 60 | 55 | if (!entry.equals(translatedEntry)) { | |
| 61 | // translate the type | 56 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); |
| 62 | Type type = new Type(constants.getFieldrefType(i)); | ||
| 63 | Type translatedType = m_translator.translateType(type); | ||
| 64 | |||
| 65 | if (!entry.equals(translatedEntry) || !type.equals(translatedType)) { | ||
| 66 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedType.toString()); | ||
| 67 | } | 57 | } |
| 68 | } | 58 | } |
| 69 | break; | 59 | break; |
| @@ -71,15 +61,14 @@ public class ClassTranslator { | |||
| 71 | case ConstPool.CONST_Methodref: | 61 | case ConstPool.CONST_Methodref: |
| 72 | case ConstPool.CONST_InterfaceMethodref: { | 62 | case ConstPool.CONST_InterfaceMethodref: { |
| 73 | 63 | ||
| 74 | // translate the name and type | 64 | // translate the name and type (ie signature) |
| 75 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( | 65 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( |
| 76 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), | 66 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), |
| 77 | editor.getMemberrefName(i), | 67 | editor.getMemberrefName(i), |
| 78 | editor.getMemberrefType(i) | 68 | editor.getMemberrefType(i) |
| 79 | ); | 69 | ); |
| 80 | BehaviorEntry translatedEntry = m_translator.translateEntry(entry); | 70 | BehaviorEntry translatedEntry = m_translator.translateEntry(entry); |
| 81 | 71 | if (!entry.equals(translatedEntry)) { | |
| 82 | if (!entry.getName().equals(translatedEntry.getName()) || !entry.getSignature().equals(translatedEntry.getSignature())) { | ||
| 83 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); | 72 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); |
| 84 | } | 73 | } |
| 85 | } | 74 | } |
| @@ -120,25 +109,18 @@ public class ClassTranslator { | |||
| 120 | } | 109 | } |
| 121 | 110 | ||
| 122 | if (entry.getSignature() != null) { | 111 | if (entry.getSignature() != null) { |
| 123 | // translate the type | 112 | // translate the signature |
| 124 | Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); | 113 | Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); |
| 125 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); | 114 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); |
| 126 | } | 115 | } |
| 127 | } | 116 | } |
| 128 | 117 | ||
| 129 | // translate all the class names referenced in the code | 118 | // translate all the class names referenced in the code |
| 130 | // the above code only changed method/field/reference names and types, but not the class names themselves | 119 | // the above code only changed method/field/reference names and types, but not the rest of the class references |
| 131 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 120 | ClassRenamer.renameClasses(c, m_translator); |
| 132 | for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { | ||
| 133 | ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); | ||
| 134 | if (!obfClassEntry.equals(deobfClassEntry)) { | ||
| 135 | map.put(obfClassEntry, deobfClassEntry); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | ClassRenamer.renameClasses(c, map); | ||
| 139 | 121 | ||
| 140 | // translate the source file attribute too | 122 | // translate the source file attribute too |
| 141 | ClassEntry deobfClassEntry = map.get(classEntry); | 123 | ClassEntry deobfClassEntry = m_translator.translateEntry(classEntry); |
| 142 | if (deobfClassEntry != null) { | 124 | if (deobfClassEntry != null) { |
| 143 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; | 125 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; |
| 144 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); | 126 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); |
diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index 7bc61839..4898e6de 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -11,6 +11,7 @@ import javassist.expr.FieldAccess; | |||
| 11 | import javassist.expr.MethodCall; | 11 | import javassist.expr.MethodCall; |
| 12 | import javassist.expr.NewExpr; | 12 | import javassist.expr.NewExpr; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 14 | import com.strobel.assembler.metadata.MethodDefinition; | 15 | import com.strobel.assembler.metadata.MethodDefinition; |
| 15 | 16 | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -54,6 +55,18 @@ public class EntryFactory { | |||
| 54 | ); | 55 | ); |
| 55 | } | 56 | } |
| 56 | 57 | ||
| 58 | public static FieldEntry getFieldEntry(FieldDefinition def) { | ||
| 59 | return new FieldEntry( | ||
| 60 | new ClassEntry(def.getDeclaringType().getInternalName()), | ||
| 61 | def.getName(), | ||
| 62 | new Type(def.getErasedSignature()) | ||
| 63 | ); | ||
| 64 | } | ||
| 65 | |||
| 66 | public static FieldEntry getFieldEntry(String className, String name, String type) { | ||
| 67 | return new FieldEntry(new ClassEntry(className), name, new Type(type)); | ||
| 68 | } | ||
| 69 | |||
| 57 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { | 70 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { |
| 58 | return new FieldEntry( | 71 | return new FieldEntry( |
| 59 | getObfClassEntry(classMapping), | 72 | getObfClassEntry(classMapping), |
| @@ -82,7 +95,7 @@ public class EntryFactory { | |||
| 82 | return new MethodEntry( | 95 | return new MethodEntry( |
| 83 | new ClassEntry(def.getDeclaringType().getInternalName()), | 96 | new ClassEntry(def.getDeclaringType().getInternalName()), |
| 84 | def.getName(), | 97 | def.getName(), |
| 85 | new Signature(def.getSignature()) | 98 | new Signature(def.getErasedSignature()) |
| 86 | ); | 99 | ); |
| 87 | } | 100 | } |
| 88 | 101 | ||
| @@ -121,7 +134,7 @@ public class EntryFactory { | |||
| 121 | } else { | 134 | } else { |
| 122 | return new ConstructorEntry( | 135 | return new ConstructorEntry( |
| 123 | new ClassEntry(def.getDeclaringType().getInternalName()), | 136 | new ClassEntry(def.getDeclaringType().getInternalName()), |
| 124 | new Signature(def.getSignature()) | 137 | new Signature(def.getErasedSignature()) |
| 125 | ); | 138 | ); |
| 126 | } | 139 | } |
| 127 | } | 140 | } |
diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java index c5ff7a7e..57ea90ce 100644 --- a/src/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java | |||
| @@ -66,7 +66,7 @@ public class MappingsChecker { | |||
| 66 | 66 | ||
| 67 | // check the fields | 67 | // check the fields |
| 68 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { | 68 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { |
| 69 | FieldEntry obfFieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); | 69 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); |
| 70 | if (!m_index.containsObfField(obfFieldEntry)) { | 70 | if (!m_index.containsObfField(obfFieldEntry)) { |
| 71 | classMapping.removeFieldMapping(fieldMapping); | 71 | classMapping.removeFieldMapping(fieldMapping); |
| 72 | m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); | 72 | m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); |
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index d7766dc8..ad6c8785 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -66,7 +66,15 @@ public class MappingsRenamer { | |||
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | public void markClassAsDeobfuscated(ClassEntry obf) { | 68 | public void markClassAsDeobfuscated(ClassEntry obf) { |
| 69 | setClassName(obf, obf.isInnerClass() ? obf.getInnermostClassName() : obf.getSimpleName()); | 69 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); |
| 70 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 71 | if (mappingChain.size() == 1) { | ||
| 72 | ClassMapping classMapping = mappingChain.get(0); | ||
| 73 | m_mappings.setClassDeobfName(classMapping, deobfName); | ||
| 74 | } else { | ||
| 75 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 76 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 77 | } | ||
| 70 | } | 78 | } |
| 71 | 79 | ||
| 72 | public void setFieldName(FieldEntry obf, String deobfName) { | 80 | public void setFieldName(FieldEntry obf, String deobfName) { |
diff --git a/src/cuchaz/enigma/mapping/ParameterizedType.java b/src/cuchaz/enigma/mapping/ParameterizedType.java new file mode 100644 index 00000000..af24ef44 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ParameterizedType.java | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.Util; | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | public class ParameterizedType extends Type { | ||
| 8 | |||
| 9 | private static final long serialVersionUID = 1758975507937309011L; | ||
| 10 | |||
| 11 | public ParameterizedType(Type other) { | ||
| 12 | super(other); | ||
| 13 | for (int i=0; i<m_parameters.size(); i++) { | ||
| 14 | m_parameters.set(i, new ParameterizedType(m_parameters.get(i))); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | public ParameterizedType(ParameterizedType type, ClassNameReplacer replacer) { | ||
| 19 | this(new Type(type, replacer)); | ||
| 20 | } | ||
| 21 | |||
| 22 | @Override | ||
| 23 | public String toString() { | ||
| 24 | if (hasParameters()) { | ||
| 25 | StringBuilder buf = new StringBuilder(); | ||
| 26 | buf.append(m_name.substring(0, m_name.length() - 1)); | ||
| 27 | buf.append("<"); | ||
| 28 | for (Type parameter : parameters()) { | ||
| 29 | buf.append(parameter.toString()); | ||
| 30 | } | ||
| 31 | buf.append(">;"); | ||
| 32 | return buf.toString(); | ||
| 33 | } else { | ||
| 34 | return m_name; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | @Override | ||
| 39 | public boolean equals(Object other) { | ||
| 40 | if (other instanceof ParameterizedType) { | ||
| 41 | return equals((ParameterizedType)other); | ||
| 42 | } | ||
| 43 | return false; | ||
| 44 | } | ||
| 45 | |||
| 46 | public boolean equals(ParameterizedType other) { | ||
| 47 | return m_name.equals(other.m_name) && m_parameters.equals(other.m_parameters); | ||
| 48 | } | ||
| 49 | |||
| 50 | public int hashCode() { | ||
| 51 | return Util.combineHashesOrdered(m_name.hashCode(), m_parameters.hashCode()); | ||
| 52 | } | ||
| 53 | |||
| 54 | } | ||
diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index ea83e40e..f4850ac3 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java | |||
| @@ -79,16 +79,6 @@ public class Signature implements Serializable { | |||
| 79 | return types; | 79 | return types; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | public Iterable<ClassEntry> classes() { | ||
| 83 | List<ClassEntry> out = Lists.newArrayList(); | ||
| 84 | for (Type type : types()) { | ||
| 85 | if (type.isClass()) { | ||
| 86 | out.add(type.getClassEntry()); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | return out; | ||
| 90 | } | ||
| 91 | |||
| 92 | @Override | 82 | @Override |
| 93 | public boolean equals(Object other) { | 83 | public boolean equals(Object other) { |
| 94 | if (other instanceof Signature) { | 84 | if (other instanceof Signature) { |
diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index 72118b04..d5300839 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | package cuchaz.enigma.mapping; | 1 | package cuchaz.enigma.mapping; |
| 2 | 2 | ||
| 3 | import java.io.Serializable; | 3 | import java.io.Serializable; |
| 4 | import java.util.List; | ||
| 4 | import java.util.Map; | 5 | import java.util.Map; |
| 5 | 6 | ||
| 7 | import com.beust.jcommander.internal.Lists; | ||
| 6 | import com.google.common.collect.Maps; | 8 | import com.google.common.collect.Maps; |
| 7 | 9 | ||
| 8 | public class Type implements Serializable { | 10 | public class Type implements Serializable { |
| @@ -84,33 +86,66 @@ public class Type implements Serializable { | |||
| 84 | throw new IllegalArgumentException("don't know how to parse: " + in); | 86 | throw new IllegalArgumentException("don't know how to parse: " + in); |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | private String m_name; | 89 | protected String m_name; |
| 90 | protected List<Type> m_parameters; | ||
| 88 | 91 | ||
| 89 | public Type(String name) { | 92 | public Type(String name) { |
| 90 | m_name = name; | 93 | m_name = null; |
| 94 | m_parameters = Lists.newArrayList(); | ||
| 95 | |||
| 96 | int start = name.indexOf('<'); | ||
| 97 | int stop = name.lastIndexOf('>'); | ||
| 98 | if (start > 0 && stop > start) { | ||
| 99 | |||
| 100 | // deal with generic parameters | ||
| 101 | m_name = name.substring(0, start) + name.substring(stop + 1); | ||
| 102 | |||
| 103 | String parameters = name.substring(start + 1, stop); | ||
| 104 | int i=0; | ||
| 105 | while (i<parameters.length()) { | ||
| 106 | String typeName = Type.parseFirst(parameters.substring(i)); | ||
| 107 | if (typeName == null) { | ||
| 108 | throw new Error("Don't know how to parse parameters: " + name); | ||
| 109 | } | ||
| 110 | m_parameters.add(new Type(typeName)); | ||
| 111 | i += typeName.length(); | ||
| 112 | } | ||
| 113 | |||
| 114 | } else { | ||
| 115 | m_name = name; | ||
| 116 | } | ||
| 91 | } | 117 | } |
| 92 | 118 | ||
| 93 | public Type(Type other) { | 119 | public Type(Type other) { |
| 94 | m_name = other.m_name; | 120 | m_name = other.m_name; |
| 121 | m_parameters = Lists.newArrayList(); | ||
| 122 | for (Type parameter : other.m_parameters) { | ||
| 123 | m_parameters.add(new Type(parameter)); | ||
| 124 | } | ||
| 95 | } | 125 | } |
| 96 | 126 | ||
| 97 | public Type(ClassEntry classEntry) { | 127 | public Type(ClassEntry classEntry) { |
| 98 | m_name = "L" + classEntry.getClassName() + ";"; | 128 | m_name = "L" + classEntry.getClassName() + ";"; |
| 99 | } | 129 | } |
| 100 | 130 | ||
| 101 | public Type(Type type, ClassNameReplacer replacer) { | 131 | public Type(Type other, ClassNameReplacer replacer) { |
| 102 | m_name = type.m_name; | 132 | m_name = other.m_name; |
| 103 | if (type.isClass()) { | 133 | if (other.isClass()) { |
| 104 | String replacedName = replacer.replace(type.getClassEntry().getClassName()); | 134 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 105 | if (replacedName != null) { | 135 | if (replacedName != null) { |
| 106 | m_name = "L" + replacedName + ";"; | 136 | m_name = "L" + replacedName + ";"; |
| 107 | } | 137 | } |
| 108 | } else if (type.isArray() && type.hasClass()) { | 138 | } else if (other.isArray() && other.hasClass()) { |
| 109 | String replacedName = replacer.replace(type.getClassEntry().getClassName()); | 139 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 110 | if (replacedName != null) { | 140 | if (replacedName != null) { |
| 111 | m_name = Type.getArrayPrefix(type.getArrayDimension()) + "L" + replacedName + ";"; | 141 | m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; |
| 112 | } | 142 | } |
| 113 | } | 143 | } |
| 144 | |||
| 145 | m_parameters = Lists.newArrayList(); | ||
| 146 | for (Type parameter : other.m_parameters) { | ||
| 147 | m_parameters.add(new Type(parameter, replacer)); | ||
| 148 | } | ||
| 114 | } | 149 | } |
| 115 | 150 | ||
| 116 | @Override | 151 | @Override |
| @@ -137,10 +172,6 @@ public class Type implements Serializable { | |||
| 137 | return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; | 172 | return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; |
| 138 | } | 173 | } |
| 139 | 174 | ||
| 140 | public boolean isTemplate() { | ||
| 141 | return m_name.charAt(0) == 'T' && m_name.charAt(m_name.length() - 1) == ';'; | ||
| 142 | } | ||
| 143 | |||
| 144 | public ClassEntry getClassEntry() { | 175 | public ClassEntry getClassEntry() { |
| 145 | if (isClass()) { | 176 | if (isClass()) { |
| 146 | String name = m_name.substring(1, m_name.length() - 1); | 177 | String name = m_name.substring(1, m_name.length() - 1); |
| @@ -160,6 +191,17 @@ public class Type implements Serializable { | |||
| 160 | } | 191 | } |
| 161 | } | 192 | } |
| 162 | 193 | ||
| 194 | public boolean isTemplate() { | ||
| 195 | return m_name.charAt(0) == 'T' && m_name.charAt(m_name.length() - 1) == ';'; | ||
| 196 | } | ||
| 197 | |||
| 198 | public String getTemplate() { | ||
| 199 | if (!isTemplate()) { | ||
| 200 | throw new IllegalStateException("not an template"); | ||
| 201 | } | ||
| 202 | return m_name.substring(1, m_name.length() - 1); | ||
| 203 | } | ||
| 204 | |||
| 163 | public boolean isArray() { | 205 | public boolean isArray() { |
| 164 | return m_name.charAt(0) == '['; | 206 | return m_name.charAt(0) == '['; |
| 165 | } | 207 | } |
| @@ -190,6 +232,14 @@ public class Type implements Serializable { | |||
| 190 | return isClass() || (isArray() && getArrayType().hasClass()); | 232 | return isClass() || (isArray() && getArrayType().hasClass()); |
| 191 | } | 233 | } |
| 192 | 234 | ||
| 235 | public boolean hasParameters() { | ||
| 236 | return !m_parameters.isEmpty(); | ||
| 237 | } | ||
| 238 | |||
| 239 | public Iterable<Type> parameters() { | ||
| 240 | return m_parameters; | ||
| 241 | } | ||
| 242 | |||
| 193 | @Override | 243 | @Override |
| 194 | public boolean equals(Object other) { | 244 | public boolean equals(Object other) { |
| 195 | if (other instanceof Type) { | 245 | if (other instanceof Type) { |
| @@ -214,7 +264,7 @@ public class Type implements Serializable { | |||
| 214 | 264 | ||
| 215 | private static String readClass(String in) { | 265 | private static String readClass(String in) { |
| 216 | // read all the characters in the buffer until we hit a ';' | 266 | // read all the characters in the buffer until we hit a ';' |
| 217 | // remember to treat parameters correctly | 267 | // include the parameters too |
| 218 | StringBuilder buf = new StringBuilder(); | 268 | StringBuilder buf = new StringBuilder(); |
| 219 | int depth = 0; | 269 | int depth = 0; |
| 220 | for (int i=0; i<in.length(); i++) { | 270 | for (int i=0; i<in.length(); i++) { |
diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java index 13971249..94bf941d 100644 --- a/test/cuchaz/enigma/TestSourceIndex.java +++ b/test/cuchaz/enigma/TestSourceIndex.java | |||
| @@ -24,7 +24,7 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 24 | 24 | ||
| 25 | public class TestSourceIndex { | 25 | public class TestSourceIndex { |
| 26 | 26 | ||
| 27 | // TEMP @Test | 27 | @Test |
| 28 | public void indexEverything() | 28 | public void indexEverything() |
| 29 | throws Exception { | 29 | throws Exception { |
| 30 | 30 | ||
diff --git a/test/cuchaz/enigma/TestTranslator.java b/test/cuchaz/enigma/TestTranslator.java index 45c69bb2..1b617405 100644 --- a/test/cuchaz/enigma/TestTranslator.java +++ b/test/cuchaz/enigma/TestTranslator.java | |||
| @@ -125,7 +125,22 @@ public class TestTranslator { | |||
| 125 | 125 | ||
| 126 | @Test | 126 | @Test |
| 127 | public void testGenerics() { | 127 | public void testGenerics() { |
| 128 | // TODO | 128 | |
| 129 | // classes | ||
| 130 | assertMapping(newClass("none/i"), newClass("deobf/I_Generics")); | ||
| 131 | assertMapping(newClass("none/i$a"), newClass("deobf/I_Generics$A_Type")); | ||
| 132 | assertMapping(newClass("none/i$b"), newClass("deobf/I_Generics$B_Generic")); | ||
| 133 | |||
| 134 | // fields | ||
| 135 | assertMapping(newField("none/i", "a", "Ljava/util/List;"), newField("deobf/I_Generics", "f1", "Ljava/util/List;")); | ||
| 136 | assertMapping(newField("none/i", "b", "Ljava/util/List;"), newField("deobf/I_Generics", "f2", "Ljava/util/List;")); | ||
| 137 | assertMapping(newField("none/i", "a", "Ljava/util/Map;"), newField("deobf/I_Generics", "f3", "Ljava/util/Map;")); | ||
| 138 | assertMapping(newField("none/i$b", "a", "Ljava/lang/Object;"), newField("deobf/I_Generics$B_Generic", "f4", "Ljava/lang/Object;")); | ||
| 139 | assertMapping(newField("none/i", "a", "Lnone/i$b;"), newField("deobf/I_Generics", "f5", "Ldeobf/I_Generics$B_Generic;")); | ||
| 140 | assertMapping(newField("none/i", "b", "Lnone/i$b;"), newField("deobf/I_Generics", "f6", "Ldeobf/I_Generics$B_Generic;")); | ||
| 141 | |||
| 142 | // methods | ||
| 143 | assertMapping(newMethod("none/i$b", "a", "()Ljava/lang/Object;"), newMethod("deobf/I_Generics$B_Generic", "m1", "()Ljava/lang/Object;")); | ||
| 129 | } | 144 | } |
| 130 | 145 | ||
| 131 | private void assertMapping(Entry obf, Entry deobf) { | 146 | private void assertMapping(Entry obf, Entry deobf) { |
diff --git a/test/cuchaz/enigma/TestType.java b/test/cuchaz/enigma/TestType.java index 93f864b0..544a10c1 100644 --- a/test/cuchaz/enigma/TestType.java +++ b/test/cuchaz/enigma/TestType.java | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import org.junit.Test; | 3 | import static cuchaz.enigma.TestEntryFactory.*; |
| 4 | |||
| 5 | import static org.hamcrest.MatcherAssert.*; | 4 | import static org.hamcrest.MatcherAssert.*; |
| 6 | import static org.hamcrest.Matchers.*; | 5 | import static org.hamcrest.Matchers.*; |
| 7 | 6 | ||
| 8 | import cuchaz.enigma.mapping.Type; | 7 | import org.junit.Test; |
| 9 | 8 | ||
| 10 | import static cuchaz.enigma.TestEntryFactory.*; | 9 | import cuchaz.enigma.mapping.ParameterizedType; |
| 10 | import cuchaz.enigma.mapping.Type; | ||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | public class TestType { | 13 | public class TestType { |
| @@ -24,6 +24,7 @@ public class TestType { | |||
| 24 | assertThat(new Type("D").isVoid(), is(false)); | 24 | assertThat(new Type("D").isVoid(), is(false)); |
| 25 | assertThat(new Type("LFoo;").isVoid(), is(false)); | 25 | assertThat(new Type("LFoo;").isVoid(), is(false)); |
| 26 | assertThat(new Type("[I").isVoid(), is(false)); | 26 | assertThat(new Type("[I").isVoid(), is(false)); |
| 27 | assertThat(new Type("TFoo;").isVoid(), is(false)); | ||
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | @Test | 30 | @Test |
| @@ -38,6 +39,7 @@ public class TestType { | |||
| 38 | assertThat(new Type("D").isPrimitive(), is(true)); | 39 | assertThat(new Type("D").isPrimitive(), is(true)); |
| 39 | assertThat(new Type("LFoo;").isPrimitive(), is(false)); | 40 | assertThat(new Type("LFoo;").isPrimitive(), is(false)); |
| 40 | assertThat(new Type("[I").isPrimitive(), is(false)); | 41 | assertThat(new Type("[I").isPrimitive(), is(false)); |
| 42 | assertThat(new Type("TFoo;").isPrimitive(), is(false)); | ||
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | @Test | 45 | @Test |
| @@ -62,13 +64,20 @@ public class TestType { | |||
| 62 | assertThat(new Type("F").isClass(), is(false)); | 64 | assertThat(new Type("F").isClass(), is(false)); |
| 63 | assertThat(new Type("D").isClass(), is(false)); | 65 | assertThat(new Type("D").isClass(), is(false)); |
| 64 | assertThat(new Type("LFoo;").isClass(), is(true)); | 66 | assertThat(new Type("LFoo;").isClass(), is(true)); |
| 67 | assertThat(new Type("LFoo<LCow;>;").isClass(), is(true)); | ||
| 68 | assertThat(new Type("LFoo<LCow;LBeer;>;").isClass(), is(true)); | ||
| 69 | assertThat(new Type("LFoo<LPair<LCow;LBeer;>;>;").isClass(), is(true)); | ||
| 65 | assertThat(new Type("[I").isClass(), is(false)); | 70 | assertThat(new Type("[I").isClass(), is(false)); |
| 71 | assertThat(new Type("TFoo;").isClass(), is(false)); | ||
| 66 | } | 72 | } |
| 67 | 73 | ||
| 68 | @Test | 74 | @Test |
| 69 | public void getClassEntry() { | 75 | public void getClassEntry() { |
| 70 | assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); | 76 | assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); |
| 71 | assertThat(new Type("LFoo<Ljava/lang/String;>;").getClassEntry(), is(newClass("Foo"))); | 77 | assertThat(new Type("LFoo<Ljava/lang/String;>;").getClassEntry(), is(newClass("Foo"))); |
| 78 | assertThat(new Type("LFoo<LCow;>;").getClassEntry(), is(newClass("Foo"))); | ||
| 79 | assertThat(new Type("LFoo<LCow;LBeer;>;").getClassEntry(), is(newClass("Foo"))); | ||
| 80 | assertThat(new Type("LFoo<LPair<LCow;LBeer;>;>;").getClassEntry(), is(newClass("Foo"))); | ||
| 72 | } | 81 | } |
| 73 | 82 | ||
| 74 | @Test | 83 | @Test |
| @@ -89,6 +98,7 @@ public class TestType { | |||
| 89 | assertThat(new Type("D").isArray(), is(false)); | 98 | assertThat(new Type("D").isArray(), is(false)); |
| 90 | assertThat(new Type("LFoo;").isArray(), is(false)); | 99 | assertThat(new Type("LFoo;").isArray(), is(false)); |
| 91 | assertThat(new Type("[I").isArray(), is(true)); | 100 | assertThat(new Type("[I").isArray(), is(true)); |
| 101 | assertThat(new Type("TFoo;").isArray(), is(false)); | ||
| 92 | } | 102 | } |
| 93 | 103 | ||
| 94 | @Test | 104 | @Test |
| @@ -107,6 +117,30 @@ public class TestType { | |||
| 107 | } | 117 | } |
| 108 | 118 | ||
| 109 | @Test | 119 | @Test |
| 120 | public void isTemplate() { | ||
| 121 | assertThat(new Type("V").isTemplate(), is(false)); | ||
| 122 | assertThat(new Type("Z").isTemplate(), is(false)); | ||
| 123 | assertThat(new Type("B").isTemplate(), is(false)); | ||
| 124 | assertThat(new Type("C").isTemplate(), is(false)); | ||
| 125 | assertThat(new Type("I").isTemplate(), is(false)); | ||
| 126 | assertThat(new Type("J").isTemplate(), is(false)); | ||
| 127 | assertThat(new Type("F").isTemplate(), is(false)); | ||
| 128 | assertThat(new Type("D").isTemplate(), is(false)); | ||
| 129 | assertThat(new Type("LFoo;").isTemplate(), is(false)); | ||
| 130 | assertThat(new Type("LFoo<LCow;>;").isTemplate(), is(false)); | ||
| 131 | assertThat(new Type("LFoo<LCow;LBeer;>;").isTemplate(), is(false)); | ||
| 132 | assertThat(new Type("LFoo<LPair<LCow;LBeer;>;>;").isTemplate(), is(false)); | ||
| 133 | assertThat(new Type("[I").isTemplate(), is(false)); | ||
| 134 | assertThat(new Type("TFoo;").isTemplate(), is(true)); | ||
| 135 | } | ||
| 136 | |||
| 137 | @Test | ||
| 138 | public void getTemplate() { | ||
| 139 | assertThat(new Type("TT;").getTemplate(), is("T")); | ||
| 140 | assertThat(new Type("TFoo;").getTemplate(), is("Foo")); | ||
| 141 | } | ||
| 142 | |||
| 143 | @Test | ||
| 110 | public void hasClass() { | 144 | public void hasClass() { |
| 111 | assertThat(new Type("LFoo;").hasClass(), is(true)); | 145 | assertThat(new Type("LFoo;").hasClass(), is(true)); |
| 112 | assertThat(new Type("LCow<LCheese;>;").hasClass(), is(true)); | 146 | assertThat(new Type("LCow<LCheese;>;").hasClass(), is(true)); |
| @@ -117,6 +151,37 @@ public class TestType { | |||
| 117 | assertThat(new Type("[I").hasClass(), is(false)); | 151 | assertThat(new Type("[I").hasClass(), is(false)); |
| 118 | assertThat(new Type("[[[I").hasClass(), is(false)); | 152 | assertThat(new Type("[[[I").hasClass(), is(false)); |
| 119 | assertThat(new Type("Z").hasClass(), is(false)); | 153 | assertThat(new Type("Z").hasClass(), is(false)); |
| 154 | assertThat(new Type("TFoo;").hasClass(), is(false)); | ||
| 155 | } | ||
| 156 | |||
| 157 | @Test | ||
| 158 | public void parameters() { | ||
| 159 | assertThat(new Type("LFoo<I>;").parameters(), contains( | ||
| 160 | new Type("I") | ||
| 161 | )); | ||
| 162 | assertThat(new Type("LFoo<IIII>;").parameters(), contains( | ||
| 163 | new Type("I"), | ||
| 164 | new Type("I"), | ||
| 165 | new Type("I"), | ||
| 166 | new Type("I") | ||
| 167 | )); | ||
| 168 | assertThat(new Type("LFoo<LBar;>;").parameters(), contains( | ||
| 169 | new Type("LBar;") | ||
| 170 | )); | ||
| 171 | assertThat(new Type("LFoo<LBar;LCow;LCheese;>;").parameters(), contains( | ||
| 172 | new Type("LBar;"), | ||
| 173 | new Type("LCow;"), | ||
| 174 | new Type("LCheese;") | ||
| 175 | )); | ||
| 176 | |||
| 177 | assertThat(new Type("LFoo<LBar<LCow;LCheese;>;>;").parameters(), contains( | ||
| 178 | new Type("LBar<LCow;LCheese;>;") | ||
| 179 | )); | ||
| 180 | |||
| 181 | assertThat(new Type("LFoo<LBar<LCow;LCheese;>;>;").parameters().iterator().next().parameters(), contains( | ||
| 182 | new Type("LCow;"), | ||
| 183 | new Type("LCheese;") | ||
| 184 | )); | ||
| 120 | } | 185 | } |
| 121 | 186 | ||
| 122 | @Test | 187 | @Test |
| @@ -171,7 +236,18 @@ public class TestType { | |||
| 171 | assertThat(Type.parseFirst("LFoo<LFoo;,LBar;>;[LFoo;"), is(answer)); | 236 | assertThat(Type.parseFirst("LFoo<LFoo;,LBar;>;[LFoo;"), is(answer)); |
| 172 | } | 237 | } |
| 173 | } | 238 | } |
| 174 | 239 | ||
| 240 | @Test | ||
| 241 | public void parseTemplate() { | ||
| 242 | final String answer = "TFoo;"; | ||
| 243 | assertThat(Type.parseFirst("TFoo;"), is(answer)); | ||
| 244 | assertThat(Type.parseFirst("TFoo;I"), is(answer)); | ||
| 245 | assertThat(Type.parseFirst("TFoo;JZ"), is(answer)); | ||
| 246 | assertThat(Type.parseFirst("TFoo;[I"), is(answer)); | ||
| 247 | assertThat(Type.parseFirst("TFoo;LFoo;"), is(answer)); | ||
| 248 | assertThat(Type.parseFirst("TFoo;[LFoo;"), is(answer)); | ||
| 249 | } | ||
| 250 | |||
| 175 | @Test | 251 | @Test |
| 176 | public void parseArray() { | 252 | public void parseArray() { |
| 177 | { | 253 | { |
| @@ -215,15 +291,90 @@ public class TestType { | |||
| 215 | assertThat(new Type("[[[I"), is(new Type("[[[I"))); | 291 | assertThat(new Type("[[[I"), is(new Type("[[[I"))); |
| 216 | assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); | 292 | assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); |
| 217 | assertThat(new Type("LFoo<LBar;>;"), is(new Type("LFoo<LBar;>;"))); | 293 | assertThat(new Type("LFoo<LBar;>;"), is(new Type("LFoo<LBar;>;"))); |
| 294 | assertThat(new Type("LFoo<LBar;>;"), is(new Type("LFoo<LCow;>;"))); | ||
| 295 | assertThat(new Type("TFoo;"), is(new Type("TFoo;"))); | ||
| 218 | 296 | ||
| 219 | assertThat(new Type("V"), is(not(new Type("I")))); | 297 | assertThat(new Type("V"), is(not(new Type("I")))); |
| 220 | assertThat(new Type("I"), is(not(new Type("J")))); | 298 | assertThat(new Type("I"), is(not(new Type("J")))); |
| 221 | assertThat(new Type("I"), is(not(new Type("LBar;")))); | 299 | assertThat(new Type("I"), is(not(new Type("LBar;")))); |
| 222 | assertThat(new Type("I"), is(not(new Type("[I")))); | 300 | assertThat(new Type("I"), is(not(new Type("[I")))); |
| 223 | assertThat(new Type("LFoo;"), is(not(new Type("LBar;")))); | 301 | assertThat(new Type("LFoo;"), is(not(new Type("LBar;")))); |
| 224 | assertThat(new Type("LFoo<LBar;>;"), is(not(new Type("LFoo<LCow;>;")))); | ||
| 225 | assertThat(new Type("[I"), is(not(new Type("[Z")))); | 302 | assertThat(new Type("[I"), is(not(new Type("[Z")))); |
| 226 | assertThat(new Type("[[[I"), is(not(new Type("[I")))); | 303 | assertThat(new Type("[[[I"), is(not(new Type("[I")))); |
| 227 | assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); | 304 | assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); |
| 305 | assertThat(new Type("TFoo;"), is(not(new Type("TBar;")))); | ||
| 306 | } | ||
| 307 | |||
| 308 | @Test | ||
| 309 | public void testToString() { | ||
| 310 | assertThat(new Type("V").toString(), is("V")); | ||
| 311 | assertThat(new Type("Z").toString(), is("Z")); | ||
| 312 | assertThat(new Type("B").toString(), is("B")); | ||
| 313 | assertThat(new Type("C").toString(), is("C")); | ||
| 314 | assertThat(new Type("I").toString(), is("I")); | ||
| 315 | assertThat(new Type("J").toString(), is("J")); | ||
| 316 | assertThat(new Type("F").toString(), is("F")); | ||
| 317 | assertThat(new Type("D").toString(), is("D")); | ||
| 318 | assertThat(new Type("LFoo;").toString(), is("LFoo;")); | ||
| 319 | assertThat(new Type("[I").toString(), is("[I")); | ||
| 320 | assertThat(new Type("[[[I").toString(), is("[[[I")); | ||
| 321 | assertThat(new Type("[LFoo;").toString(), is("[LFoo;")); | ||
| 322 | assertThat(new Type("LFoo<LBar;>;").toString(), is("LFoo;")); | ||
| 323 | assertThat(new Type("LFoo<LCow;LCheese;>;").toString(), is("LFoo;")); | ||
| 324 | assertThat(new Type("LFoo<LPair<LCow;LCheese;>;>;").toString(), is("LFoo;")); | ||
| 325 | assertThat(new Type("TFoo;").toString(), is("TFoo;")); | ||
| 326 | } | ||
| 327 | |||
| 328 | private ParameterizedType ptype(String name) { | ||
| 329 | return new ParameterizedType(new Type(name)); | ||
| 330 | } | ||
| 331 | |||
| 332 | @Test | ||
| 333 | public void equalsWithParameters() { | ||
| 334 | assertThat(ptype("V"), is(ptype("V"))); | ||
| 335 | assertThat(ptype("Z"), is(ptype("Z"))); | ||
| 336 | assertThat(ptype("B"), is(ptype("B"))); | ||
| 337 | assertThat(ptype("C"), is(ptype("C"))); | ||
| 338 | assertThat(ptype("I"), is(ptype("I"))); | ||
| 339 | assertThat(ptype("J"), is(ptype("J"))); | ||
| 340 | assertThat(ptype("F"), is(ptype("F"))); | ||
| 341 | assertThat(ptype("D"), is(ptype("D"))); | ||
| 342 | assertThat(ptype("LFoo;"), is(ptype("LFoo;"))); | ||
| 343 | assertThat(ptype("[I"), is(ptype("[I"))); | ||
| 344 | assertThat(ptype("[[[I"), is(ptype("[[[I"))); | ||
| 345 | assertThat(ptype("[LFoo;"), is(ptype("[LFoo;"))); | ||
| 346 | assertThat(ptype("LFoo<LBar;>;"), is(ptype("LFoo<LBar;>;"))); | ||
| 347 | assertThat(ptype("TFoo;"), is(ptype("TFoo;"))); | ||
| 348 | |||
| 349 | assertThat(ptype("V"), is(not(ptype("I")))); | ||
| 350 | assertThat(ptype("I"), is(not(ptype("J")))); | ||
| 351 | assertThat(ptype("I"), is(not(ptype("LBar;")))); | ||
| 352 | assertThat(ptype("I"), is(not(ptype("[I")))); | ||
| 353 | assertThat(ptype("LFoo;"), is(not(ptype("LBar;")))); | ||
| 354 | assertThat(ptype("[I"), is(not(ptype("[Z")))); | ||
| 355 | assertThat(ptype("[[[I"), is(not(ptype("[I")))); | ||
| 356 | assertThat(ptype("[LFoo;"), is(not(ptype("[LBar;")))); | ||
| 357 | assertThat(ptype("LFoo<LBar;>;"), is(not(ptype("LFoo<LCow;>;")))); | ||
| 358 | assertThat(ptype("TFoo;"), is(not(ptype("TBar;")))); | ||
| 359 | } | ||
| 360 | |||
| 361 | @Test | ||
| 362 | public void testToStringWithParams() { | ||
| 363 | assertThat(ptype("V").toString(), is("V")); | ||
| 364 | assertThat(ptype("Z").toString(), is("Z")); | ||
| 365 | assertThat(ptype("B").toString(), is("B")); | ||
| 366 | assertThat(ptype("C").toString(), is("C")); | ||
| 367 | assertThat(ptype("I").toString(), is("I")); | ||
| 368 | assertThat(ptype("J").toString(), is("J")); | ||
| 369 | assertThat(ptype("F").toString(), is("F")); | ||
| 370 | assertThat(ptype("D").toString(), is("D")); | ||
| 371 | assertThat(ptype("LFoo;").toString(), is("LFoo;")); | ||
| 372 | assertThat(ptype("[I").toString(), is("[I")); | ||
| 373 | assertThat(ptype("[[[I").toString(), is("[[[I")); | ||
| 374 | assertThat(ptype("[LFoo;").toString(), is("[LFoo;")); | ||
| 375 | assertThat(ptype("LFoo<LBar;>;").toString(), is("LFoo<LBar;>;")); | ||
| 376 | assertThat(ptype("LFoo<LCow;LCheese;>;").toString(), is("LFoo<LCow;LCheese;>;")); | ||
| 377 | assertThat(ptype("LFoo<LPair<LCow;LCheese;>;>;").toString(), is("LFoo<LPair<LCow;LCheese;>;>;")); | ||
| 378 | assertThat(ptype("TFoo;").toString(), is("TFoo;")); | ||
| 228 | } | 379 | } |
| 229 | } | 380 | } |
diff --git a/test/cuchaz/enigma/resources/translation.mappings b/test/cuchaz/enigma/resources/translation.mappings index 55bd7e5c..db78c19d 100644 --- a/test/cuchaz/enigma/resources/translation.mappings +++ b/test/cuchaz/enigma/resources/translation.mappings | |||
| @@ -29,4 +29,13 @@ CLASS none/g deobf/G_OuterClass | |||
| 29 | CLASS none/g$b$a A_NamedInnerClass | 29 | CLASS none/g$b$a A_NamedInnerClass |
| 30 | FIELD a f4 I | 30 | FIELD a f4 I |
| 31 | CLASS none/h | 31 | CLASS none/h |
| 32 | CLASS none/i I_Generics | 32 | CLASS none/i deobf/I_Generics |
| 33 | CLASS none/i$a A_Type | ||
| 34 | CLASS none/i$b B_Generic | ||
| 35 | FIELD a f4 Ljava/lang/Object; | ||
| 36 | METHOD a m1 ()Ljava/lang/Object; | ||
| 37 | FIELD a f1 Ljava/util/List; | ||
| 38 | FIELD b f2 Ljava/util/List; | ||
| 39 | FIELD a f3 Ljava/util/Map; | ||
| 40 | FIELD a f5 Lnone/i$b; | ||
| 41 | FIELD b f6 Lnone/i$b; | ||