summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java9
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java31
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java7
-rw-r--r--src/cuchaz/enigma/bytecode/ClassRenamer.java164
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java42
-rw-r--r--src/cuchaz/enigma/mapping/EntryFactory.java17
-rw-r--r--src/cuchaz/enigma/mapping/MappingsChecker.java2
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java10
-rw-r--r--src/cuchaz/enigma/mapping/ParameterizedType.java54
-rw-r--r--src/cuchaz/enigma/mapping/Signature.java10
-rw-r--r--src/cuchaz/enigma/mapping/Type.java78
-rw-r--r--test/cuchaz/enigma/TestSourceIndex.java2
-rw-r--r--test/cuchaz/enigma/TestTranslator.java17
-rw-r--r--test/cuchaz/enigma/TestType.java163
-rw-r--r--test/cuchaz/enigma/resources/translation.mappings11
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;
28import javassist.NotFoundException; 28import javassist.NotFoundException;
29import javassist.bytecode.AccessFlag; 29import javassist.bytecode.AccessFlag;
30import javassist.bytecode.Descriptor; 30import javassist.bytecode.Descriptor;
31import javassist.bytecode.EnclosingMethodAttribute;
31import javassist.bytecode.FieldInfo; 32import javassist.bytecode.FieldInfo;
32import javassist.bytecode.InnerClassesAttribute; 33import javassist.bytecode.InnerClassesAttribute;
33import javassist.expr.ConstructorCall; 34import 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;
30import cuchaz.enigma.mapping.ConstructorEntry; 30import cuchaz.enigma.mapping.ConstructorEntry;
31import cuchaz.enigma.mapping.EntryFactory; 31import cuchaz.enigma.mapping.EntryFactory;
32import cuchaz.enigma.mapping.FieldEntry; 32import cuchaz.enigma.mapping.FieldEntry;
33import cuchaz.enigma.mapping.Type;
34 33
35public class SourceIndexClassVisitor extends SourceIndexVisitor { 34public 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;
23import com.google.common.collect.Sets; 23import com.google.common.collect.Sets;
24 24
25import cuchaz.enigma.mapping.ClassEntry; 25import cuchaz.enigma.mapping.ClassEntry;
26import cuchaz.enigma.mapping.ClassNameReplacer;
27import cuchaz.enigma.mapping.ParameterizedType;
28import cuchaz.enigma.mapping.Translator;
29import cuchaz.enigma.mapping.Type;
26 30
27public class ClassRenamer { 31public 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 ******************************************************************************/
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import java.util.Map;
14
15import javassist.CtBehavior; 13import javassist.CtBehavior;
16import javassist.CtClass; 14import javassist.CtClass;
17import javassist.CtField; 15import javassist.CtField;
@@ -19,9 +17,6 @@ import javassist.CtMethod;
19import javassist.bytecode.ConstPool; 17import javassist.bytecode.ConstPool;
20import javassist.bytecode.Descriptor; 18import javassist.bytecode.Descriptor;
21import javassist.bytecode.SourceFileAttribute; 19import javassist.bytecode.SourceFileAttribute;
22
23import com.google.common.collect.Maps;
24
25import cuchaz.enigma.mapping.BehaviorEntry; 20import cuchaz.enigma.mapping.BehaviorEntry;
26import cuchaz.enigma.mapping.ClassEntry; 21import cuchaz.enigma.mapping.ClassEntry;
27import cuchaz.enigma.mapping.EntryFactory; 22import 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;
11import javassist.expr.MethodCall; 11import javassist.expr.MethodCall;
12import javassist.expr.NewExpr; 12import javassist.expr.NewExpr;
13 13
14import com.strobel.assembler.metadata.FieldDefinition;
14import com.strobel.assembler.metadata.MethodDefinition; 15import com.strobel.assembler.metadata.MethodDefinition;
15 16
16import cuchaz.enigma.analysis.JarIndex; 17import 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 @@
1package cuchaz.enigma.mapping;
2
3import cuchaz.enigma.Util;
4
5
6
7public 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 @@
1package cuchaz.enigma.mapping; 1package cuchaz.enigma.mapping;
2 2
3import java.io.Serializable; 3import java.io.Serializable;
4import java.util.List;
4import java.util.Map; 5import java.util.Map;
5 6
7import com.beust.jcommander.internal.Lists;
6import com.google.common.collect.Maps; 8import com.google.common.collect.Maps;
7 9
8public class Type implements Serializable { 10public 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
25public class TestSourceIndex { 25public 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 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import org.junit.Test; 3import static cuchaz.enigma.TestEntryFactory.*;
4
5import static org.hamcrest.MatcherAssert.*; 4import static org.hamcrest.MatcherAssert.*;
6import static org.hamcrest.Matchers.*; 5import static org.hamcrest.Matchers.*;
7 6
8import cuchaz.enigma.mapping.Type; 7import org.junit.Test;
9 8
10import static cuchaz.enigma.TestEntryFactory.*; 9import cuchaz.enigma.mapping.ParameterizedType;
10import cuchaz.enigma.mapping.Type;
11 11
12 12
13public class TestType { 13public 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
31CLASS none/h 31CLASS none/h
32CLASS none/i I_Generics 32CLASS 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;