summaryrefslogtreecommitdiff
path: root/src/cuchaz
diff options
context:
space:
mode:
authorGravatar jeff2015-03-16 19:22:22 -0400
committerGravatar jeff2015-03-16 19:22:22 -0400
commit5e3743a0aca3529eacf9be400c8b8d7547f66e7f (patch)
treeea601747547f78e1b83ab828650932126440e221 /src/cuchaz
parentupdate to new javassist version to (hopefully) get bug fixes (diff)
downloadenigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.gz
enigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.xz
enigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.zip
started adding minimal support for generics
fixed mark-as-deobfuscated issue
Diffstat (limited to 'src/cuchaz')
-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
11 files changed, 294 insertions, 130 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 5a23ce5..b63f163 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 7ebbd97..e255468 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 f4f4956..f4202b5 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 e9cdea3..8bc084d 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 94ab2c4..7952577 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 7bc6183..4898e6d 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 c5ff7a7..57ea90c 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 d7766dc..ad6c878 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 0000000..af24ef4
--- /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 ea83e40..f4850ac 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 72118b0..d530083 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++) {