summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2015-03-17 00:05:10 -0400
committerGravatar jeff2015-03-17 00:05:10 -0400
commit03f9a58ced909336f6477d3b69a960d777cce1fa (patch)
treec553514605f77d366f288cdcfbba24444411a457 /src
parentstarted adding minimal support for generics (diff)
downloadenigma-03f9a58ced909336f6477d3b69a960d777cce1fa.tar.gz
enigma-03f9a58ced909336f6477d3b69a960d777cce1fa.tar.xz
enigma-03f9a58ced909336f6477d3b69a960d777cce1fa.zip
slightly better support for generics
still needs work
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/bytecode/ClassRenamer.java309
1 files changed, 230 insertions, 79 deletions
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java
index 8bc084d3..d88daa5b 100644
--- a/src/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -10,18 +10,28 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import java.lang.reflect.InvocationTargetException;
14import java.lang.reflect.Method;
15import java.util.HashMap;
16import java.util.List;
13import java.util.Map; 17import java.util.Map;
14import java.util.Set;
15 18
16import javassist.ClassMap;
17import javassist.CtClass; 19import javassist.CtClass;
20import javassist.bytecode.AttributeInfo;
21import javassist.bytecode.BadBytecode;
22import javassist.bytecode.ByteArray;
23import javassist.bytecode.ClassFile;
24import javassist.bytecode.CodeAttribute;
18import javassist.bytecode.ConstPool; 25import javassist.bytecode.ConstPool;
19import javassist.bytecode.Descriptor; 26import javassist.bytecode.Descriptor;
27import javassist.bytecode.FieldInfo;
20import javassist.bytecode.InnerClassesAttribute; 28import javassist.bytecode.InnerClassesAttribute;
21 29import javassist.bytecode.LocalVariableTypeAttribute;
22import com.google.common.collect.Maps; 30import javassist.bytecode.MethodInfo;
23import com.google.common.collect.Sets; 31import javassist.bytecode.SignatureAttribute;
24 32import javassist.bytecode.SignatureAttribute.ClassSignature;
33import javassist.bytecode.SignatureAttribute.MethodSignature;
34import javassist.bytecode.SignatureAttribute.ObjectType;
25import cuchaz.enigma.mapping.ClassEntry; 35import cuchaz.enigma.mapping.ClassEntry;
26import cuchaz.enigma.mapping.ClassNameReplacer; 36import cuchaz.enigma.mapping.ClassNameReplacer;
27import cuchaz.enigma.mapping.ParameterizedType; 37import cuchaz.enigma.mapping.ParameterizedType;
@@ -30,6 +40,107 @@ import cuchaz.enigma.mapping.Type;
30 40
31public class ClassRenamer { 41public class ClassRenamer {
32 42
43 private static enum SignatureType {
44 Class {
45
46 @Override
47 public void rename(SignatureAttribute attribute, ReplacerClassMap map) {
48 renameClassSignatureAttribute(attribute, map);
49 }
50 },
51 Field {
52
53 @Override
54 public void rename(SignatureAttribute attribute, ReplacerClassMap map) {
55 renameFieldSignatureAttribute(attribute, map);
56 }
57 },
58 Method {
59
60 @Override
61 public void rename(SignatureAttribute attribute, ReplacerClassMap map) {
62 renameMethodSignatureAttribute(attribute, map);
63 }
64 };
65
66 public abstract void rename(SignatureAttribute attribute, ReplacerClassMap map);
67 }
68
69 private static class ReplacerClassMap extends HashMap<String,String> {
70
71 private static final long serialVersionUID = 317915213205066168L;
72
73 private ClassNameReplacer m_replacer;
74
75 public ReplacerClassMap(ClassNameReplacer replacer) {
76 m_replacer = replacer;
77 }
78
79 @Override
80 public String get(Object obj) {
81 if (obj instanceof String) {
82 return get((String)obj);
83 } else if (obj instanceof ObjectType) {
84 return get((ObjectType)obj);
85 }
86 return null;
87 }
88
89 public String get(String typeName) {
90
91 // javassist doesn't give us the class framing, add it
92 typeName = "L" + typeName + ";";
93
94 String out = getFramed(typeName);
95 if (out == null) {
96 return null;
97 }
98
99 // javassist doesn't want the class framing, so remove it
100 out = out.substring(1, out.length() - 1);
101
102 return out;
103 }
104
105 public String getFramed(String typeName) {
106 ParameterizedType type = new ParameterizedType(new Type(typeName));
107 ParameterizedType renamedType = new ParameterizedType(type, m_replacer);
108 if (!type.equals(renamedType)) {
109 return renamedType.toString();
110 }
111 return null;
112 }
113
114 public String get(ObjectType type) {
115
116 // we can deal with the ones that start with a class
117 String signature = type.encode();
118 if (signature.startsWith("L") || signature.startsWith("[")) {
119
120 // TEMP: skip special characters for now
121 if (signature.indexOf('*') >= 0 || signature.indexOf('+') >= 0 || signature.indexOf('-') >= 0) {
122 System.out.println("Skipping translating: " + signature);
123 return null;
124 }
125
126 // replace inner class / with $
127 int pos = signature.indexOf("$");
128 if (pos >= 0) {
129 signature = signature.substring(0, pos + 1) + signature.substring(pos, signature.length()).replace('/', '$');
130 }
131
132 return getFramed(signature);
133 } else if (signature.startsWith("T")) {
134 // don't need to care about template names
135 return null;
136 } else {
137 // TEMP
138 System.out.println("Skipping translating: " + signature);
139 return null;
140 }
141 }
142 }
143
33 public static void renameClasses(CtClass c, final Translator translator) { 144 public static void renameClasses(CtClass c, final Translator translator) {
34 renameClasses(c, new ClassNameReplacer() { 145 renameClasses(c, new ClassNameReplacer() {
35 @Override 146 @Override
@@ -69,86 +180,42 @@ public class ClassRenamer {
69 }); 180 });
70 } 181 }
71 182
183 @SuppressWarnings("unchecked")
72 public static void renameClasses(CtClass c, ClassNameReplacer replacer) { 184 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) {
84 185
85 // TODO: might have to scan SignatureAttributes directly because javassist is buggy 186 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
86 187
87 // get the class types that javassist knows about 188 ReplacerClassMap map = new ReplacerClassMap(replacer);
88 final Set<ParameterizedType> types = Sets.newHashSet(); 189 ClassFile classFile = c.getClassFile();
89 ClassMap map = new ClassMap() {
90 @Override
91 public Object get(Object obj) {
92 if (obj instanceof String) {
93 String str = (String)obj;
94
95 // sometimes javasist gives us dot-separated classes... whadda hell?
96 str = str.replace('.', '/');
97
98 // skip weird types
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);
104 return null;
105 }
106
107 ParameterizedType type = new ParameterizedType(new Type("L" + str + ";"));
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 }
118
119 types.add(type);
120 }
121 return null;
122 }
123
124 private static final long serialVersionUID = -202160293602070641L;
125 };
126 c.replaceClassName(map);
127 190
128 return types; 191 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
129 } 192 ConstPool constPool = c.getClassFile().getConstPool();
130 193 constPool.renameClass(map);
131 public static void renameTypes(CtClass c, Map<ParameterizedType,ParameterizedType> map) {
132 194
133 // convert the type map to a javassist class map 195 // rename class attributes
134 ClassMap nameMap = new ClassMap(); 196 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
135 for (Map.Entry<ParameterizedType,ParameterizedType> entry : map.entrySet()) { 197
136 String source = entry.getKey().toString(); 198 // rename methods
137 String dest = entry.getValue().toString(); 199 for (MethodInfo methodInfo : (List<MethodInfo>)classFile.getMethods()) {
138 200 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
139 // don't forget to chop off the L ... ; 201 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
140 // javassist doesn't want it there 202 }
141 source = source.substring(1, source.length() - 1); 203
142 dest = dest.substring(1, dest.length() - 1); 204 // rename fields
143 205 for (FieldInfo fieldInfo : (List<FieldInfo>)classFile.getFields()) {
144 nameMap.put(source, dest); 206 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
207 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
208 }
209
210 // rename the class name itself last
211 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
212 // we only want to replace exactly this class name
213 String newName = replacer.replace(Descriptor.toJvmName(c.getName()));
214 if (newName != null) {
215 c.setName(Descriptor.toJavaName(newName));
145 } 216 }
146 217
147 // replace!!
148 c.replaceClassName(nameMap);
149
150 // replace simple names in the InnerClasses attribute too 218 // replace simple names in the InnerClasses attribute too
151 ConstPool constants = c.getClassFile().getConstPool();
152 InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); 219 InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag);
153 if (attr != null) { 220 if (attr != null) {
154 for (int i = 0; i < attr.tableLength(); i++) { 221 for (int i = 0; i < attr.tableLength(); i++) {
@@ -158,7 +225,7 @@ public class ClassRenamer {
158 225
159 if (attr.innerNameIndex(i) != 0) { 226 if (attr.innerNameIndex(i) != 0) {
160 // update the inner name 227 // update the inner name
161 attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); 228 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
162 } 229 }
163 230
164 /* DEBUG 231 /* DEBUG
@@ -167,4 +234,88 @@ public class ClassRenamer {
167 } 234 }
168 } 235 }
169 } 236 }
237
238 @SuppressWarnings("unchecked")
239 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) {
240 try {
241
242 // make the rename class method accessible
243 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class);
244 renameClassMethod.setAccessible(true);
245
246 for (AttributeInfo attribute : attributes) {
247 if (attribute instanceof SignatureAttribute) {
248 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
249 SignatureAttribute signatureAttribute = (SignatureAttribute)attribute;
250 type.rename(signatureAttribute, map);
251 } else if (attribute instanceof CodeAttribute) {
252 // code attributes have signature attributes too (indirectly)
253 CodeAttribute codeAttribute = (CodeAttribute)attribute;
254 renameAttributes(codeAttribute.getAttributes(), map, type);
255 } else if (attribute instanceof LocalVariableTypeAttribute) {
256 // lvt attributes have signature attributes too
257 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute)attribute;
258 renameLocalVariableTypeAttribute(localVariableAttribute, map);
259 } else {
260 renameClassMethod.invoke(attribute, map);
261 }
262 }
263
264 } catch(NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
265 throw new Error("Unable to call javassist methods by reflection!", ex);
266 }
267 }
268
269 private static void renameClassSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) {
270 try {
271 ClassSignature classSignature = SignatureAttribute.toClassSignature(attribute.getSignature());
272 // TODO: do class signatures
273 } catch (BadBytecode ex) {
274 throw new Error("Unable to parse class signature: " + attribute.getSignature(), ex);
275 }
276 }
277
278 private static void renameFieldSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) {
279 try {
280 ObjectType fieldSignature = SignatureAttribute.toFieldSignature(attribute.getSignature());
281 String newSignature = map.get(fieldSignature);
282 if (newSignature != null) {
283 attribute.setSignature(newSignature);
284 }
285 } catch (BadBytecode ex) {
286 throw new Error("Unable to parse field signature: " + attribute.getSignature(), ex);
287 }
288 }
289
290 private static void renameMethodSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) {
291 try {
292 MethodSignature methodSignature = SignatureAttribute.toMethodSignature(attribute.getSignature());
293 // TODO: do method signatures
294 } catch (BadBytecode ex) {
295 throw new Error("Unable to parse method signature: " + attribute.getSignature(), ex);
296 }
297 }
298
299 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
300 // adapted from LocalVariableAttribute.renameClass()
301 ConstPool cp = attribute.getConstPool();
302 int n = attribute.tableLength();
303 byte[] info = attribute.get();
304 for (int i = 0; i < n; ++i) {
305 int pos = i * 10 + 2;
306 int index = ByteArray.readU16bit(info, pos + 6);
307 if (index != 0) {
308 String desc = cp.getUtf8Info(index);
309 try {
310 ObjectType fieldSignature = SignatureAttribute.toFieldSignature(desc);
311 String newDesc = map.get(fieldSignature);
312 if (newDesc != null) {
313 ByteArray.write16bit(cp.addUtf8Info(newDesc), info, pos + 6);
314 }
315 } catch (BadBytecode ex) {
316 throw new Error("Unable to parse field signature: " + desc, ex);
317 }
318 }
319 }
320 }
170} 321}