summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java')
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java539
1 files changed, 0 insertions, 539 deletions
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
deleted file mode 100644
index 62a838d..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
+++ /dev/null
@@ -1,539 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode;
13
14import cuchaz.enigma.mapping.ClassEntry;
15import cuchaz.enigma.mapping.ClassNameReplacer;
16import cuchaz.enigma.mapping.Mappings;
17import cuchaz.enigma.mapping.Translator;
18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
21import javassist.Modifier;
22import javassist.bytecode.*;
23import javassist.bytecode.SignatureAttribute.*;
24
25import java.lang.reflect.InvocationTargetException;
26import java.lang.reflect.Method;
27import java.util.Arrays;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
32public class ClassRenamer {
33
34 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) {
35 int mod = -1;
36 if (obj instanceof CtField)
37 mod = ((CtField) obj).getModifiers();
38 else if (obj instanceof CtBehavior)
39 mod = ((CtBehavior) obj).getModifiers();
40 else if (obj instanceof CtClass)
41 mod = ((CtClass) obj).getModifiers();
42
43 if (mod != -1) {
44 switch (modifier) {
45 case PRIVATE:
46 mod = Modifier.setPrivate(mod);
47 break;
48 case PROTECTED:
49 mod = Modifier.setProtected(mod);
50 break;
51 case PUBLIC:
52 mod = Modifier.setPublic(mod);
53 break;
54 default:
55 break;
56 }
57 if (obj instanceof CtField)
58 ((CtField) obj).setModifiers(mod);
59 else if (obj instanceof CtBehavior)
60 ((CtBehavior) obj).setModifiers(mod);
61 else
62 ((CtClass) obj).setModifiers(mod);
63 }
64 }
65
66 public static void renameClasses(CtClass c, final Translator translator) {
67 renameClasses(c, className -> {
68 ClassEntry entry = translator.translateEntry(new ClassEntry(className));
69 if (entry != null) {
70 return entry.getName();
71 }
72 return null;
73 });
74 }
75
76 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) {
77 renameClasses(c, className -> {
78 ClassEntry entry = new ClassEntry(className);
79 if (entry.isInDefaultPackage()) {
80 return newPackageName + "/" + entry.getName();
81 }
82 return null;
83 });
84 }
85
86 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) {
87 renameClasses(c, className -> {
88 ClassEntry entry = new ClassEntry(className);
89 if (entry.getPackageName().equals(oldPackageName)) {
90 return entry.getSimpleName();
91 }
92 return null;
93 });
94 }
95
96 @SuppressWarnings("unchecked")
97 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
98
99 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
100
101 ReplacerClassMap map = new ReplacerClassMap(replacer);
102 ClassFile classFile = c.getClassFile();
103
104 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
105 ConstPool constPool = c.getClassFile().getConstPool();
106 constPool.renameClass(map);
107
108 // rename class attributes
109 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
110
111 // rename methods
112 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
113 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
114 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
115 }
116
117 // rename fields
118 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
119 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
120 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
121 }
122
123 // rename the class name itself last
124 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
125 // we only want to replace exactly this class name
126 String newName = renameClassName(c.getName(), map);
127 if (newName != null) {
128 c.setName(newName);
129 }
130
131 // replace simple names in the InnerClasses attribute too
132 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
133 if (attr != null) {
134 for (int i = 0; i < attr.tableLength(); i++) {
135
136 String innerName = attr.innerClass(i);
137 // get the inner class full name (which has already been translated)
138 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
139
140 if (attr.innerNameIndex(i) != 0) {
141 // update the inner name
142 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
143 }
144
145 /* DEBUG
146 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i)));
147 */
148 }
149 }
150 }
151
152 @SuppressWarnings("unchecked")
153 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) {
154 try {
155
156 // make the rename class method accessible
157 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class);
158 renameClassMethod.setAccessible(true);
159
160 for (AttributeInfo attribute : attributes) {
161 if (attribute instanceof SignatureAttribute) {
162 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
163 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute;
164 String newSignature = type.rename(signatureAttribute.getSignature(), map);
165 if (newSignature != null) {
166 signatureAttribute.setSignature(newSignature);
167 }
168 } else if (attribute instanceof CodeAttribute) {
169 // code attributes have signature attributes too (indirectly)
170 CodeAttribute codeAttribute = (CodeAttribute) attribute;
171 renameAttributes(codeAttribute.getAttributes(), map, type);
172 } else if (attribute instanceof LocalVariableTypeAttribute) {
173 // lvt attributes have signature attributes too
174 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute;
175 renameLocalVariableTypeAttribute(localVariableAttribute, map);
176 } else {
177 renameClassMethod.invoke(attribute, map);
178 }
179 }
180
181 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
182 throw new Error("Unable to call javassist methods by reflection!", ex);
183 }
184 }
185
186 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
187
188 // adapted from LocalVariableAttribute.renameClass()
189 ConstPool cp = attribute.getConstPool();
190 int n = attribute.tableLength();
191 byte[] info = attribute.get();
192 for (int i = 0; i < n; ++i) {
193 int pos = i * 10 + 2;
194 int index = ByteArray.readU16bit(info, pos + 6);
195 if (index != 0) {
196 String signature = cp.getUtf8Info(index);
197 String newSignature = renameLocalVariableSignature(signature, map);
198 if (newSignature != null) {
199 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6);
200 }
201 }
202 }
203 }
204
205 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) {
206
207 // for some reason, signatures with . in them don't count as field signatures
208 // looks like anonymous classes delimit with . in stead of $
209 // convert the . to $, but keep track of how many we replace
210 // we need to put them back after we translate
211 int start = signature.lastIndexOf('$') + 1;
212 int numConverted = 0;
213 StringBuilder buf = new StringBuilder(signature);
214 for (int i = buf.length() - 1; i >= start; i--) {
215 char c = buf.charAt(i);
216 if (c == '.') {
217 buf.setCharAt(i, '$');
218 numConverted++;
219 }
220 }
221 signature = buf.toString();
222
223 // translate
224 String newSignature = renameFieldSignature(signature, map);
225 if (newSignature != null) {
226
227 // put the delimiters back
228 buf = new StringBuilder(newSignature);
229 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) {
230 char c = buf.charAt(i);
231 if (c == '$') {
232 buf.setCharAt(i, '.');
233 numConverted--;
234 }
235 }
236 assert (numConverted == 0);
237 newSignature = buf.toString();
238
239 return newSignature;
240 }
241
242 return null;
243 }
244
245 private static String renameClassSignature(String signature, ReplacerClassMap map) {
246 try {
247 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map);
248 return type.encode();
249 } catch (BadBytecode ex) {
250 throw new Error("Can't parse field signature: " + signature);
251 }
252 }
253
254 private static String renameFieldSignature(String signature, ReplacerClassMap map) {
255 try {
256 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map);
257 if (type != null) {
258 return type.encode();
259 }
260 return null;
261 } catch (BadBytecode ex) {
262 throw new Error("Can't parse class signature: " + signature);
263 }
264 }
265
266 private static String renameMethodSignature(String signature, ReplacerClassMap map) {
267 try {
268 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map);
269 return type.encode();
270 } catch (BadBytecode ex) {
271 throw new Error("Can't parse method signature: " + signature);
272 }
273 }
274
275 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) {
276 if (typeParamTypes != null) {
277 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length);
278 for (int i = 0; i < typeParamTypes.length; i++) {
279 TypeParameter newParamType = renameType(typeParamTypes[i], map);
280 if (newParamType != null) {
281 typeParamTypes[i] = newParamType;
282 }
283 }
284 }
285 return typeParamTypes;
286 }
287
288 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) {
289
290 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map);
291
292 ClassType superclassType = type.getSuperClass();
293 if (superclassType != ClassType.OBJECT) {
294 ClassType newSuperclassType = renameType(superclassType, map);
295 if (newSuperclassType != null) {
296 superclassType = newSuperclassType;
297 }
298 }
299
300 ClassType[] interfaceTypes = type.getInterfaces();
301 if (interfaceTypes != null) {
302 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
303 for (int i = 0; i < interfaceTypes.length; i++) {
304 ClassType newInterfaceType = renameType(interfaceTypes[i], map);
305 if (newInterfaceType != null) {
306 interfaceTypes[i] = newInterfaceType;
307 }
308 }
309 }
310
311 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes);
312 }
313
314 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) {
315
316 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map);
317
318 Type[] paramTypes = type.getParameterTypes();
319 if (paramTypes != null) {
320 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
321 for (int i = 0; i < paramTypes.length; i++) {
322 Type newParamType = renameType(paramTypes[i], map);
323 if (newParamType != null) {
324 paramTypes[i] = newParamType;
325 }
326 }
327 }
328
329 Type returnType = type.getReturnType();
330 if (returnType != null) {
331 Type newReturnType = renameType(returnType, map);
332 if (newReturnType != null) {
333 returnType = newReturnType;
334 }
335 }
336
337 ObjectType[] exceptionTypes = type.getExceptionTypes();
338 if (exceptionTypes != null) {
339 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length);
340 for (int i = 0; i < exceptionTypes.length; i++) {
341 ObjectType newExceptionType = renameType(exceptionTypes[i], map);
342 if (newExceptionType != null) {
343 exceptionTypes[i] = newExceptionType;
344 }
345 }
346 }
347
348 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes);
349 }
350
351 private static Type renameType(Type type, ReplacerClassMap map) {
352 if (type instanceof ObjectType) {
353 return renameType((ObjectType) type, map);
354 } else if (type instanceof BaseType) {
355 return renameType((BaseType) type, map);
356 } else {
357 throw new Error("Don't know how to rename type " + type.getClass());
358 }
359 }
360
361 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) {
362 if (type instanceof ArrayType) {
363 return renameType((ArrayType) type, map);
364 } else if (type instanceof ClassType) {
365 return renameType((ClassType) type, map);
366 } else if (type instanceof TypeVariable) {
367 return renameType((TypeVariable) type, map);
368 } else {
369 throw new Error("Don't know how to rename type " + type.getClass());
370 }
371 }
372
373 private static BaseType renameType(BaseType type, ReplacerClassMap map) {
374 // don't have to rename primitives
375 return null;
376 }
377
378 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) {
379 // don't have to rename template args
380 return null;
381 }
382
383 private static ClassType renameType(ClassType type, ReplacerClassMap map) {
384
385 // translate type args
386 TypeArgument[] args = type.getTypeArguments();
387 if (args != null) {
388 args = Arrays.copyOf(args, args.length);
389 for (int i = 0; i < args.length; i++) {
390 TypeArgument newType = renameType(args[i], map);
391 if (newType != null) {
392 args[i] = newType;
393 }
394 }
395 }
396
397 if (type instanceof NestedClassType) {
398 NestedClassType nestedType = (NestedClassType) type;
399
400 // translate the name
401 String name = getClassName(type);
402 String newName = map.get(name);
403 if (newName != null) {
404 name = new ClassEntry(newName).getInnermostClassName();
405 }
406
407 // translate the parent class too
408 ClassType parent = renameType(nestedType.getDeclaringClass(), map);
409 if (parent == null) {
410 parent = nestedType.getDeclaringClass();
411 }
412
413 return new NestedClassType(parent, name, args);
414 } else {
415
416 // translate the name
417 String name = type.getName();
418 String newName = renameClassName(name, map);
419 if (newName != null) {
420 name = newName;
421 }
422
423 return new ClassType(name, args);
424 }
425 }
426
427 private static String getClassName(ClassType type) {
428 if (type instanceof NestedClassType) {
429 NestedClassType nestedType = (NestedClassType) type;
430 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$'));
431 } else {
432 return Descriptor.toJvmName(type.getName());
433 }
434 }
435
436 private static String renameClassName(String name, ReplacerClassMap map) {
437 String newName = map.get(Descriptor.toJvmName(name));
438 if (newName != null) {
439 return Descriptor.toJavaName(newName);
440 }
441 return null;
442 }
443
444 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) {
445 ObjectType subType = type.getType();
446 if (subType != null) {
447 ObjectType newSubType = renameType(subType, map);
448 if (newSubType != null) {
449 switch (type.getKind()) {
450 case ' ':
451 return new TypeArgument(newSubType);
452 case '+':
453 return TypeArgument.subclassOf(newSubType);
454 case '-':
455 return TypeArgument.superOf(newSubType);
456 default:
457 throw new Error("Unknown type kind: " + type.getKind());
458 }
459 }
460 }
461 return null;
462 }
463
464 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) {
465 Type newSubType = renameType(type.getComponentType(), map);
466 if (newSubType != null) {
467 return new ArrayType(type.getDimension(), newSubType);
468 }
469 return null;
470 }
471
472 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) {
473
474 ObjectType superclassType = type.getClassBound();
475 if (superclassType != null) {
476 ObjectType newSuperclassType = renameType(superclassType, map);
477 if (newSuperclassType != null) {
478 superclassType = newSuperclassType;
479 }
480 }
481
482 ObjectType[] interfaceTypes = type.getInterfaceBound();
483 if (interfaceTypes != null) {
484 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
485 for (int i = 0; i < interfaceTypes.length; i++) {
486 ObjectType newInterfaceType = renameType(interfaceTypes[i], map);
487 if (newInterfaceType != null) {
488 interfaceTypes[i] = newInterfaceType;
489 }
490 }
491 }
492
493 return new TypeParameter(type.getName(), superclassType, interfaceTypes);
494 }
495
496 private enum SignatureType {
497 Class {
498 @Override
499 public String rename(String signature, ReplacerClassMap map) {
500 return renameClassSignature(signature, map);
501 }
502 },
503 Field {
504 @Override
505 public String rename(String signature, ReplacerClassMap map) {
506 return renameFieldSignature(signature, map);
507 }
508 },
509 Method {
510 @Override
511 public String rename(String signature, ReplacerClassMap map) {
512 return renameMethodSignature(signature, map);
513 }
514 };
515
516 public abstract String rename(String signature, ReplacerClassMap map);
517 }
518
519 private static class ReplacerClassMap extends HashMap<String, String> {
520
521 private ClassNameReplacer replacer;
522
523 public ReplacerClassMap(ClassNameReplacer replacer) {
524 this.replacer = replacer;
525 }
526
527 @Override
528 public String get(Object obj) {
529 if (obj instanceof String) {
530 return get((String) obj);
531 }
532 return null;
533 }
534
535 public String get(String className) {
536 return replacer.replace(className);
537 }
538 }
539}