summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2015-03-18 00:06:33 -0400
committerGravatar jeff2015-03-18 00:06:33 -0400
commit0c15b6485cfeff42a438e8387d209055ad7d7704 (patch)
treea88cf4632882657f58d802a608c98b910daa1069
parentparsing generic signatures is tricky. don't write custom code to do it. switc... (diff)
downloadenigma-0c15b6485cfeff42a438e8387d209055ad7d7704.tar.gz
enigma-0c15b6485cfeff42a438e8387d209055ad7d7704.tar.xz
enigma-0c15b6485cfeff42a438e8387d209055ad7d7704.zip
added full rename support for classes buried in generic signatures
oi, what a pain in the ass...
-rw-r--r--src/cuchaz/enigma/bytecode/ClassRenamer.java378
1 files changed, 280 insertions, 98 deletions
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java
index d7acd309..8d25e722 100644
--- a/src/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -12,6 +12,7 @@ package cuchaz.enigma.bytecode;
12 12
13import java.lang.reflect.InvocationTargetException; 13import java.lang.reflect.InvocationTargetException;
14import java.lang.reflect.Method; 14import java.lang.reflect.Method;
15import java.util.Arrays;
15import java.util.HashMap; 16import java.util.HashMap;
16import java.util.List; 17import java.util.List;
17import java.util.Map; 18import java.util.Map;
@@ -29,13 +30,19 @@ import javassist.bytecode.InnerClassesAttribute;
29import javassist.bytecode.LocalVariableTypeAttribute; 30import javassist.bytecode.LocalVariableTypeAttribute;
30import javassist.bytecode.MethodInfo; 31import javassist.bytecode.MethodInfo;
31import javassist.bytecode.SignatureAttribute; 32import javassist.bytecode.SignatureAttribute;
33import javassist.bytecode.SignatureAttribute.ArrayType;
34import javassist.bytecode.SignatureAttribute.BaseType;
32import javassist.bytecode.SignatureAttribute.ClassSignature; 35import javassist.bytecode.SignatureAttribute.ClassSignature;
36import javassist.bytecode.SignatureAttribute.ClassType;
33import javassist.bytecode.SignatureAttribute.MethodSignature; 37import javassist.bytecode.SignatureAttribute.MethodSignature;
38import javassist.bytecode.SignatureAttribute.NestedClassType;
34import javassist.bytecode.SignatureAttribute.ObjectType; 39import javassist.bytecode.SignatureAttribute.ObjectType;
40import javassist.bytecode.SignatureAttribute.Type;
41import javassist.bytecode.SignatureAttribute.TypeArgument;
42import javassist.bytecode.SignatureAttribute.TypeVariable;
35import cuchaz.enigma.mapping.ClassEntry; 43import cuchaz.enigma.mapping.ClassEntry;
36import cuchaz.enigma.mapping.ClassNameReplacer; 44import cuchaz.enigma.mapping.ClassNameReplacer;
37import cuchaz.enigma.mapping.Translator; 45import cuchaz.enigma.mapping.Translator;
38import cuchaz.enigma.mapping.Type;
39 46
40public class ClassRenamer { 47public class ClassRenamer {
41 48
@@ -43,26 +50,26 @@ public class ClassRenamer {
43 Class { 50 Class {
44 51
45 @Override 52 @Override
46 public void rename(SignatureAttribute attribute, ReplacerClassMap map) { 53 public String rename(String signature, ReplacerClassMap map) {
47 renameClassSignatureAttribute(attribute, map); 54 return renameClassSignature(signature, map);
48 } 55 }
49 }, 56 },
50 Field { 57 Field {
51 58
52 @Override 59 @Override
53 public void rename(SignatureAttribute attribute, ReplacerClassMap map) { 60 public String rename(String signature, ReplacerClassMap map) {
54 renameFieldSignatureAttribute(attribute, map); 61 return renameFieldSignature(signature, map);
55 } 62 }
56 }, 63 },
57 Method { 64 Method {
58 65
59 @Override 66 @Override
60 public void rename(SignatureAttribute attribute, ReplacerClassMap map) { 67 public String rename(String signature, ReplacerClassMap map) {
61 renameMethodSignatureAttribute(attribute, map); 68 return renameMethodSignature(signature, map);
62 } 69 }
63 }; 70 };
64 71
65 public abstract void rename(SignatureAttribute attribute, ReplacerClassMap map); 72 public abstract String rename(String signature, ReplacerClassMap map);
66 } 73 }
67 74
68 private static class ReplacerClassMap extends HashMap<String,String> { 75 private static class ReplacerClassMap extends HashMap<String,String> {
@@ -79,66 +86,12 @@ public class ClassRenamer {
79 public String get(Object obj) { 86 public String get(Object obj) {
80 if (obj instanceof String) { 87 if (obj instanceof String) {
81 return get((String)obj); 88 return get((String)obj);
82 } else if (obj instanceof ObjectType) {
83 return get((ObjectType)obj);
84 } 89 }
85 return null; 90 return null;
86 } 91 }
87 92
88 public String get(String typeName) { 93 public String get(String className) {
89 94 return m_replacer.replace(className);
90 // javassist doesn't give us the class framing, add it
91 typeName = "L" + typeName + ";";
92
93 String out = getFramed(typeName);
94 if (out == null) {
95 return null;
96 }
97
98 // javassist doesn't want the class framing, so remove it
99 out = out.substring(1, out.length() - 1);
100
101 return out;
102 }
103
104 public String getFramed(String typeName) {
105 Type type = new Type(typeName);
106 Type renamedType = new Type(type, m_replacer);
107 if (!type.equals(renamedType)) {
108 return renamedType.toString();
109 }
110 return null;
111 }
112
113 public String get(ObjectType type) {
114
115 // we can deal with the ones that start with a class
116 String signature = type.encode();
117 /*
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 */
138 // TEMP
139 System.out.println("Skipping translating: " + signature);
140 return null;
141 //}
142 } 95 }
143 } 96 }
144 97
@@ -248,7 +201,10 @@ public class ClassRenamer {
248 if (attribute instanceof SignatureAttribute) { 201 if (attribute instanceof SignatureAttribute) {
249 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell 202 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
250 SignatureAttribute signatureAttribute = (SignatureAttribute)attribute; 203 SignatureAttribute signatureAttribute = (SignatureAttribute)attribute;
251 type.rename(signatureAttribute, map); 204 String newSignature = type.rename(signatureAttribute.getSignature(), map);
205 if (newSignature != null) {
206 signatureAttribute.setSignature(newSignature);
207 }
252 } else if (attribute instanceof CodeAttribute) { 208 } else if (attribute instanceof CodeAttribute) {
253 // code attributes have signature attributes too (indirectly) 209 // code attributes have signature attributes too (indirectly)
254 CodeAttribute codeAttribute = (CodeAttribute)attribute; 210 CodeAttribute codeAttribute = (CodeAttribute)attribute;
@@ -267,56 +223,282 @@ public class ClassRenamer {
267 } 223 }
268 } 224 }
269 225
270 private static void renameClassSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { 226 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
227
228 // adapted from LocalVariableAttribute.renameClass()
229 ConstPool cp = attribute.getConstPool();
230 int n = attribute.tableLength();
231 byte[] info = attribute.get();
232 for (int i = 0; i < n; ++i) {
233 int pos = i * 10 + 2;
234 int index = ByteArray.readU16bit(info, pos + 6);
235 if (index != 0) {
236 String signature = cp.getUtf8Info(index);
237 String newSignature = renameLocalVariableSignature(signature, map);
238 if (newSignature != null) {
239 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6);
240 }
241 }
242 }
243 }
244
245 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) {
246
247 // for some reason, signatures with . in them don't count as field signatures
248 // looks like anonymous classes delimit with . in stead of $
249 // convert the . to $, but keep track of how many we replace
250 // we need to put them back after we translate
251 int start = signature.lastIndexOf('$') + 1;
252 int numConverted = 0;
253 StringBuilder buf = new StringBuilder(signature);
254 for (int i=buf.length()-1; i>=start; i--) {
255 char c = buf.charAt(i);
256 if (c == '.') {
257 buf.setCharAt(i, '$');
258 numConverted++;
259 }
260 }
261 signature = buf.toString();
262
263 // translate
264 String newSignature = renameFieldSignature(signature, map);
265 if (newSignature != null) {
266
267 // put the delimiters back
268 buf = new StringBuilder(newSignature);
269 for (int i=buf.length()-1; i>=0 && numConverted > 0; i--) {
270 char c = buf.charAt(i);
271 if (c == '$') {
272 buf.setCharAt(i, '.');
273 numConverted--;
274 }
275 }
276 assert(numConverted == 0);
277 newSignature = buf.toString();
278
279 return newSignature;
280 }
281
282 return null;
283 }
284
285 private static String renameClassSignature(String signature, ReplacerClassMap map) {
271 try { 286 try {
272 ClassSignature classSignature = SignatureAttribute.toClassSignature(attribute.getSignature()); 287 return getSignature(renameType(SignatureAttribute.toClassSignature(signature), map));
273 // TODO: do class signatures
274 } catch (BadBytecode ex) { 288 } catch (BadBytecode ex) {
275 throw new Error("Unable to parse class signature: " + attribute.getSignature(), ex); 289 throw new Error("Can't parse field signature: " + signature);
276 } 290 }
277 } 291 }
278 292
279 private static void renameFieldSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { 293 private static String renameFieldSignature(String signature, ReplacerClassMap map) {
280 try { 294 try {
281 ObjectType fieldSignature = SignatureAttribute.toFieldSignature(attribute.getSignature()); 295 return getSignature(renameType(SignatureAttribute.toFieldSignature(signature), map));
282 String newSignature = map.get(fieldSignature);
283 if (newSignature != null) {
284 attribute.setSignature(newSignature);
285 }
286 } catch (BadBytecode ex) { 296 } catch (BadBytecode ex) {
287 throw new Error("Unable to parse field signature: " + attribute.getSignature(), ex); 297 throw new Error("Can't parse class signature: " + signature);
288 } 298 }
289 } 299 }
290 300
291 private static void renameMethodSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { 301 private static String renameMethodSignature(String signature, ReplacerClassMap map) {
292 try { 302 try {
293 MethodSignature methodSignature = SignatureAttribute.toMethodSignature(attribute.getSignature()); 303 return getSignature(renameType(SignatureAttribute.toMethodSignature(signature), map));
294 // TODO: do method signatures
295 } catch (BadBytecode ex) { 304 } catch (BadBytecode ex) {
296 throw new Error("Unable to parse method signature: " + attribute.getSignature(), ex); 305 throw new Error("Can't parse method signature: " + signature);
297 } 306 }
298 } 307 }
299 308
300 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { 309 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) {
301 // adapted from LocalVariableAttribute.renameClass() 310
302 ConstPool cp = attribute.getConstPool(); 311 // NOTE: don't have to translate type parameters
303 int n = attribute.tableLength(); 312
304 byte[] info = attribute.get(); 313 // translate superclass
305 for (int i = 0; i < n; ++i) { 314 ClassType superclassType = type.getSuperClass();
306 int pos = i * 10 + 2; 315 if (superclassType != ClassType.OBJECT) {
307 int index = ByteArray.readU16bit(info, pos + 6); 316 ClassType newSuperclassType = renameType(superclassType, map);
308 if (index != 0) { 317 if (newSuperclassType != null) {
309 String desc = cp.getUtf8Info(index); 318 superclassType = newSuperclassType;
310 try { 319 }
311 ObjectType fieldSignature = SignatureAttribute.toFieldSignature(desc); 320 }
312 String newDesc = map.get(fieldSignature); 321
313 if (newDesc != null) { 322 // translate interfaces
314 ByteArray.write16bit(cp.addUtf8Info(newDesc), info, pos + 6); 323 ClassType[] interfaceTypes = type.getInterfaces();
315 } 324 if (interfaceTypes != null) {
316 } catch (BadBytecode ex) { 325 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
317 throw new Error("Unable to parse field signature: " + desc, ex); 326 for (int i=0; i<interfaceTypes.length; i++) {
327 ClassType newInterfaceType = renameType(interfaceTypes[i], map);
328 if (newInterfaceType != null) {
329 interfaceTypes[i] = newInterfaceType;
318 } 330 }
319 } 331 }
320 } 332 }
333
334 return new ClassSignature(type.getParameters(), superclassType, interfaceTypes);
335 }
336
337 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) {
338
339 // don't need to rename type params here either
340
341 Type[] paramTypes = type.getParameterTypes();
342 if (paramTypes != null) {
343 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
344 for (int i=0; i<paramTypes.length; i++) {
345 Type newParamType = renameType(paramTypes[i], map);
346 if (newParamType != null) {
347 paramTypes[i] = newParamType;
348 }
349 }
350 }
351
352 Type returnType = type.getReturnType();
353 if (returnType != null) {
354 Type newReturnType = renameType(returnType, map);
355 if (newReturnType != null) {
356 returnType = newReturnType;
357 }
358 }
359
360 ObjectType[] exceptionTypes = type.getExceptionTypes();
361 if (exceptionTypes != null) {
362 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length);
363 for (int i=0; i<exceptionTypes.length; i++) {
364 ObjectType newExceptionType = renameType(exceptionTypes[i], map);
365 if (newExceptionType != null) {
366 exceptionTypes[i] = newExceptionType;
367 }
368 }
369 }
370
371 return new MethodSignature(type.getTypeParameters(), paramTypes, returnType, exceptionTypes);
372 }
373
374 private static Type renameType(Type type, ReplacerClassMap map) {
375 if (type instanceof ObjectType) {
376 return renameType((ObjectType)type, map);
377 } else if (type instanceof BaseType) {
378 return renameType((BaseType)type, map);
379 } else {
380 throw new Error("Don't know how to rename type " + type.getClass());
381 }
382 }
383
384 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) {
385 if (type instanceof ArrayType) {
386 return renameType((ArrayType)type, map);
387 } else if (type instanceof ClassType) {
388 return renameType((ClassType)type, map);
389 } else if (type instanceof TypeVariable) {
390 return renameType((TypeVariable)type, map);
391 } else {
392 throw new Error("Don't know how to rename type " + type.getClass());
393 }
394 }
395
396 private static BaseType renameType(BaseType type, ReplacerClassMap map) {
397 // don't have to rename primitives
398 return null;
399 }
400
401 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) {
402 // don't have to rename template args
403 return null;
404 }
405
406 private static ClassType renameType(ClassType type, ReplacerClassMap map) {
407
408 // translate type args
409 TypeArgument[] args = type.getTypeArguments();
410 if (args != null) {
411 args = Arrays.copyOf(args, args.length);
412 for (int i=0; i<args.length; i++) {
413 TypeArgument newType = renameType(args[i], map);
414 if (newType != null) {
415 args[i] = newType;
416 }
417 }
418 }
419
420 if (type instanceof NestedClassType) {
421 NestedClassType nestedType = (NestedClassType)type;
422
423 // translate the name
424 String name = nestedType.getName();
425 String newName = map.get(getClassName(type));
426 if (newName != null) {
427 name = new ClassEntry(newName).getInnermostClassName();
428 }
429
430 // translate the parent class too
431 ClassType parent = renameType(nestedType.getDeclaringClass(), map);
432 if (parent == null) {
433 parent = nestedType.getDeclaringClass();
434 }
435
436 return new NestedClassType(parent, name, args);
437 } else {
438
439 // translate the name
440 String name = Descriptor.toJvmName(type.getName());
441 String newName = map.get(name);
442 if (newName != null) {
443 name = Descriptor.toJavaName(newName);
444 }
445
446 return new ClassType(name, args);
447 }
448 }
449
450 private static String getClassName(ClassType type) {
451 if (type instanceof NestedClassType) {
452 NestedClassType nestedType = (NestedClassType)type;
453 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName());
454 } else {
455 return Descriptor.toJvmName(type.getName());
456 }
457 }
458
459 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) {
460 ObjectType subType = type.getType();
461 if (subType != null) {
462 ObjectType newSubType = renameType(subType, map);
463 if (newSubType != null) {
464 switch (type.getKind()) {
465 case ' ': return new TypeArgument(newSubType);
466 case '+': return TypeArgument.subclassOf(newSubType);
467 case '-': return TypeArgument.superOf(newSubType);
468 default:
469 throw new Error("Unknown type kind: " + type.getKind());
470 }
471 }
472 }
473 return null;
474 }
475
476 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) {
477 Type newSubType = renameType(type.getComponentType(), map);
478 if (newSubType != null) {
479 return new ArrayType(type.getDimension(), newSubType);
480 }
481 return null;
482 }
483
484 private static String getSignature(ObjectType type) {
485 if (type == null) {
486 return null;
487 }
488 return type.encode();
489 }
490
491 private static String getSignature(ClassSignature type) {
492 if (type == null) {
493 return null;
494 }
495 return type.encode();
496 }
497
498 private static String getSignature(MethodSignature type) {
499 if (type == null) {
500 return null;
501 }
502 return type.encode();
321 } 503 }
322} 504}