summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/bytecode
diff options
context:
space:
mode:
authorGravatar Modmuss502018-07-18 13:46:00 +0100
committerGravatar GitHub2018-07-18 13:46:00 +0100
commit1ebe691c12f68beea378b133ddc4bcbde7f3f795 (patch)
treefb051d9fde5644bd144a7e9d7bcecc70a256359c /src/main/java/cuchaz/enigma/bytecode
parentRecursively rebuild method names (diff)
parentUpdate version number (diff)
downloadenigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.gz
enigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.xz
enigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.zip
Merge pull request #62 from OpenModLoader/asm
ASM based class translator
Diffstat (limited to 'src/main/java/cuchaz/enigma/bytecode')
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/AccessFlags.java80
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java54
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java58
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java539
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java264
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InfoType.java266
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java85
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java56
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java124
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java57
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java56
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java29
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java161
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java144
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java142
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java62
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java43
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java113
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java33
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java191
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java129
25 files changed, 646 insertions, 2340 deletions
diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
new file mode 100644
index 0000000..21b2489
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
@@ -0,0 +1,80 @@
1package cuchaz.enigma.bytecode;
2
3import org.objectweb.asm.Opcodes;
4
5import java.lang.reflect.Modifier;
6
7public class AccessFlags {
8 private int flags;
9
10 public AccessFlags(int flags) {
11 this.flags = flags;
12 }
13
14 public boolean isPrivate() {
15 return Modifier.isPrivate(this.flags);
16 }
17
18 public boolean isProtected() {
19 return Modifier.isProtected(this.flags);
20 }
21
22 public boolean isPublic() {
23 return Modifier.isPublic(this.flags);
24 }
25
26 public boolean isSynthetic() {
27 return (this.flags & Opcodes.ACC_SYNTHETIC) != 0;
28 }
29
30 public boolean isStatic() {
31 return Modifier.isStatic(this.flags);
32 }
33
34 public boolean isEnum() {
35 return (flags & Opcodes.ACC_ENUM) != 0;
36 }
37
38 public AccessFlags setPrivate() {
39 this.setVisibility(Opcodes.ACC_PRIVATE);
40 return this;
41 }
42
43 public AccessFlags setProtected() {
44 this.setVisibility(Opcodes.ACC_PROTECTED);
45 return this;
46 }
47
48 public AccessFlags setPublic() {
49 this.setVisibility(Opcodes.ACC_PUBLIC);
50 return this;
51 }
52
53 public AccessFlags setBridged() {
54 this.setVisibility(Opcodes.ACC_BRIDGE);
55 return this;
56 }
57
58 public void setVisibility(int visibility) {
59 this.resetVisibility();
60 this.flags |= visibility;
61 }
62
63 private void resetVisibility() {
64 this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
65 }
66
67 public int getFlags() {
68 return this.flags;
69 }
70
71 @Override
72 public boolean equals(Object obj) {
73 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags;
74 }
75
76 @Override
77 public int hashCode() {
78 return flags;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
index 6ec576e..9ed6db9 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
@@ -11,41 +11,39 @@
11 11
12package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
13 13
14import javassist.CtBehavior; 14import org.objectweb.asm.ClassVisitor;
15import javassist.CtClass; 15import org.objectweb.asm.FieldVisitor;
16import javassist.CtField; 16import org.objectweb.asm.MethodVisitor;
17import javassist.bytecode.AccessFlag;
18import javassist.bytecode.InnerClassesAttribute;
19 17
20public class ClassProtectifier { 18public class ClassProtectifier extends ClassVisitor {
21 19
22 public static CtClass protectify(CtClass c) { 20 public ClassProtectifier(int api, ClassVisitor cv) {
23 21 super(api, cv);
24 // protectify all the fields 22 }
25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(protectify(field.getModifiers()));
27 }
28 23
29 // protectify all the methods and constructors 24 @Override
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 25 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 behavior.setModifiers(protectify(behavior.getModifiers())); 26 access = protectify(access);
32 } 27 return super.visitMethod(access, name, desc, signature, exceptions);
28 }
33 29
34 // protectify all the inner classes 30 @Override
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 31 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
36 if (attr != null) { 32 access = protectify(access);
37 for (int i = 0; i < attr.tableLength(); i++) { 33 return super.visitField(access, name, desc, signature, value);
38 attr.setAccessFlags(i, protectify(attr.accessFlags(i))); 34 }
39 }
40 }
41 35
42 return c; 36 @Override
37 public void visitInnerClass(String name, String outerName, String innerName, int access) {
38 access = protectify(access);
39 super.visitInnerClass(name, outerName, innerName, access);
43 } 40 }
44 41
45 private static int protectify(int flags) { 42 private static int protectify(int access) {
46 if (AccessFlag.isPrivate(flags)) { 43 AccessFlags accessFlags = new AccessFlags(access);
47 flags = AccessFlag.setProtected(flags); 44 if (accessFlags.isPrivate()) {
45 accessFlags.setProtected();
48 } 46 }
49 return flags; 47 return accessFlags.getFlags();
50 } 48 }
51} 49}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
index d627fe9..64de788 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
@@ -11,41 +11,45 @@
11 11
12package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
13 13
14import javassist.CtBehavior; 14import org.objectweb.asm.ClassVisitor;
15import javassist.CtClass; 15import org.objectweb.asm.FieldVisitor;
16import javassist.CtField; 16import org.objectweb.asm.MethodVisitor;
17import javassist.bytecode.AccessFlag;
18import javassist.bytecode.InnerClassesAttribute;
19 17
20public class ClassPublifier { 18public class ClassPublifier extends ClassVisitor {
21 19
22 public static CtClass publify(CtClass c) { 20 public ClassPublifier(int api, ClassVisitor cv) {
21 super(api, cv);
22 }
23 23
24 // publify all the fields 24 @Override
25 for (CtField field : c.getDeclaredFields()) { 25 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
26 field.setModifiers(publify(field.getModifiers())); 26 access = publify(access);
27 } 27 super.visit(version, access, name, signature, superName, interfaces);
28 }
28 29
29 // publify all the methods and constructors 30 @Override
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 31 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
31 behavior.setModifiers(publify(behavior.getModifiers())); 32 access = publify(access);
32 } 33 return super.visitField(access, name, desc, signature, value);
34 }
33 35
34 // publify all the inner classes 36 @Override
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 37 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
36 if (attr != null) { 38 access = publify(access);
37 for (int i = 0; i < attr.tableLength(); i++) { 39 return super.visitMethod(access, name, desc, signature, exceptions);
38 attr.setAccessFlags(i, publify(attr.accessFlags(i))); 40 }
39 }
40 }
41 41
42 return c; 42 @Override
43 public void visitInnerClass(String name, String outerName, String innerName, int access) {
44 access = publify(access);
45 super.visitInnerClass(name, outerName, innerName, access);
43 } 46 }
44 47
45 private static int publify(int flags) { 48 private static int publify(int access) {
46 if (!AccessFlag.isPublic(flags)) { 49 AccessFlags accessFlags = new AccessFlags(access);
47 flags = AccessFlag.setPublic(flags); 50 if (!accessFlags.isPublic()) {
51 accessFlags.setPublic();
48 } 52 }
49 return flags; 53 return accessFlags.getFlags();
50 } 54 }
51} 55}
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}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
deleted file mode 100644
index 1932730..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
+++ /dev/null
@@ -1,264 +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.bytecode.accessors.ClassInfoAccessor;
15import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
16import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
17import javassist.bytecode.ConstPool;
18import javassist.bytecode.Descriptor;
19
20import java.io.DataInputStream;
21import java.io.DataOutputStream;
22import java.lang.reflect.Constructor;
23import java.lang.reflect.Field;
24import java.lang.reflect.Method;
25import java.util.HashMap;
26
27public class ConstPoolEditor {
28
29 private static Method getItem;
30 private static Method addItem;
31 private static Method addItem0;
32 private static Field items;
33 private static Field cache;
34 private static Field numItems;
35 private static Field objects;
36 private static Field elements;
37 private static Method methodWritePool;
38 private static Constructor<ConstPool> constructorPool;
39
40 static {
41 try {
42 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class);
43 getItem.setAccessible(true);
44
45 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo"));
46 addItem.setAccessible(true);
47
48 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo"));
49 addItem0.setAccessible(true);
50
51 items = ConstPool.class.getDeclaredField("items");
52 items.setAccessible(true);
53
54 cache = ConstPool.class.getDeclaredField("itemsCache");
55 cache.setAccessible(true);
56
57 numItems = ConstPool.class.getDeclaredField("numOfItems");
58 numItems.setAccessible(true);
59
60 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects");
61 objects.setAccessible(true);
62
63 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements");
64 elements.setAccessible(true);
65
66 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class);
67 methodWritePool.setAccessible(true);
68
69 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class);
70 constructorPool.setAccessible(true);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75
76 private ConstPool pool;
77
78 public ConstPoolEditor(ConstPool pool) {
79 this.pool = pool;
80 }
81
82 public static ConstPool readPool(DataInputStream in) {
83 try {
84 return constructorPool.newInstance(in);
85 } catch (Exception ex) {
86 throw new Error(ex);
87 }
88 }
89
90 public static ConstPool newConstPool() {
91 // const pool expects the name of a class to initialize itself
92 // but we want an empty pool
93 // so give it a bogus name, and then clear the entries afterwards
94 ConstPool pool = new ConstPool("a");
95
96 ConstPoolEditor editor = new ConstPoolEditor(pool);
97 int size = pool.getSize();
98 for (int i = 0; i < size - 1; i++) {
99 editor.removeLastItem();
100 }
101
102 // make sure the pool is actually empty
103 // although, in this case "empty" means one thing in it
104 // the JVM spec says index 0 should be reserved
105 assert (pool.getSize() == 1);
106 assert (editor.getItem(0) == null);
107 assert (editor.getItem(1) == null);
108 assert (editor.getItem(2) == null);
109 assert (editor.getItem(3) == null);
110
111 // also, clear the cache
112 editor.getCache().clear();
113
114 return pool;
115 }
116
117 public void writePool(DataOutputStream out) {
118 try {
119 methodWritePool.invoke(this.pool, out);
120 } catch (Exception ex) {
121 throw new Error(ex);
122 }
123 }
124
125 public String getMemberrefClassname(int memberrefIndex) {
126 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex)));
127 }
128
129 public String getMemberrefName(int memberrefIndex) {
130 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex)));
131 }
132
133 public String getMemberrefType(int memberrefIndex) {
134 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex)));
135 }
136
137 public ConstInfoAccessor getItem(int index) {
138 try {
139 Object entry = getItem.invoke(this.pool, index);
140 if (entry == null) {
141 return null;
142 }
143 return new ConstInfoAccessor(entry);
144 } catch (Exception ex) {
145 throw new Error(ex);
146 }
147 }
148
149 public int addItem(Object item) {
150 try {
151 return (Integer) addItem.invoke(this.pool, item);
152 } catch (Exception ex) {
153 throw new Error(ex);
154 }
155 }
156
157 public int addItemForceNew(Object item) {
158 try {
159 return (Integer) addItem0.invoke(this.pool, item);
160 } catch (Exception ex) {
161 throw new Error(ex);
162 }
163 }
164
165 @SuppressWarnings("rawtypes")
166 public void removeLastItem() {
167 try {
168 // remove the item from the cache
169 HashMap cache = getCache();
170 if (cache != null) {
171 Object item = getItem(this.pool.getSize() - 1);
172 cache.remove(item);
173 }
174
175 // remove the actual item
176 // based off of LongVector.addElement()
177 Object item = items.get(this.pool);
178 Object[][] object = (Object[][]) objects.get(items);
179 int numElements = (Integer) elements.get(items) - 1;
180 int nth = numElements >> 7;
181 int offset = numElements & (128 - 1);
182 object[nth][offset] = null;
183
184 // decrement the number of items
185 elements.set(item, numElements);
186 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1);
187 } catch (Exception ex) {
188 throw new Error(ex);
189 }
190 }
191
192 @SuppressWarnings("rawtypes")
193 public HashMap getCache() {
194 try {
195 return (HashMap) cache.get(this.pool);
196 } catch (Exception ex) {
197 throw new Error(ex);
198 }
199 }
200
201 @SuppressWarnings({ "rawtypes", "unchecked" })
202 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) {
203 // NOTE: when changing values, we always need to copy-on-write
204 try {
205 // get the memberref item
206 Object item = getItem(memberrefIndex).getItem();
207
208 // update the cache
209 HashMap cache = getCache();
210 if (cache != null) {
211 cache.remove(item);
212 }
213
214 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType));
215
216 // update the cache
217 if (cache != null) {
218 cache.put(item, item);
219 }
220 } catch (Exception ex) {
221 throw new Error(ex);
222 }
223
224 // make sure the change worked
225 assert (newName.equals(getMemberrefName(memberrefIndex)));
226 assert (newType.equals(getMemberrefType(memberrefIndex)));
227 }
228
229 @SuppressWarnings({ "rawtypes", "unchecked" })
230 public void changeClassName(int classNameIndex, String newName) {
231 // NOTE: when changing values, we always need to copy-on-write
232 try {
233 // get the class item
234 Object item = getItem(classNameIndex).getItem();
235
236 // update the cache
237 HashMap cache = getCache();
238 if (cache != null) {
239 cache.remove(item);
240 }
241
242 // add the new name and repoint the name-and-type to it
243 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName));
244
245 // update the cache
246 if (cache != null) {
247 cache.put(item, item);
248 }
249 } catch (Exception ex) {
250 throw new Error(ex);
251 }
252 }
253
254 public String dump() {
255 StringBuilder buf = new StringBuilder();
256 for (int i = 1; i < this.pool.getSize(); i++) {
257 buf.append(String.format("%4d", i));
258 buf.append(" ");
259 buf.append(getItem(i));
260 buf.append("\n");
261 }
262 return buf.toString();
263 }
264}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
deleted file mode 100644
index 9013d58..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java
+++ /dev/null
@@ -1,266 +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 com.google.common.collect.Maps;
15import cuchaz.enigma.bytecode.accessors.*;
16
17import java.util.Collection;
18import java.util.Map;
19
20public enum InfoType {
21
22 Utf8Info(1),
23 IntegerInfo(3),
24 FloatInfo(4),
25 LongInfo(5),
26 DoubleInfo(6),
27 ClassInfo(7) {
28 @Override
29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
31 gatherIndexTree(indices, editor, accessor.getNameIndex());
32 }
33
34 @Override
35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
38 }
39
40 @Override
41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
45 }
46 },
47 StringInfo(8) {
48 @Override
49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
51 gatherIndexTree(indices, editor, accessor.getStringIndex());
52 }
53
54 @Override
55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex()));
58 }
59
60 @Override
61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex());
64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
65 }
66 },
67 FieldRefInfo(9) {
68 @Override
69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
71 gatherIndexTree(indices, editor, accessor.getClassIndex());
72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
73 }
74
75 @Override
76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex()));
79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
80 }
81
82 @Override
83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex());
86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
88 }
89 },
90 // same as FieldRefInfo
91 MethodRefInfo(10) {
92 @Override
93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
94 FieldRefInfo.gatherIndexTree(indices, editor, entry);
95 }
96
97 @Override
98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
99 FieldRefInfo.remapIndices(map, entry);
100 }
101
102 @Override
103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
104 return FieldRefInfo.subIndicesAreValid(entry, pool);
105 }
106 },
107 // same as FieldRefInfo
108 InterfaceMethodRefInfo(11) {
109 @Override
110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
111 FieldRefInfo.gatherIndexTree(indices, editor, entry);
112 }
113
114 @Override
115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
116 FieldRefInfo.remapIndices(map, entry);
117 }
118
119 @Override
120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
121 return FieldRefInfo.subIndicesAreValid(entry, pool);
122 }
123 },
124 NameAndTypeInfo(12) {
125 @Override
126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
128 gatherIndexTree(indices, editor, accessor.getNameIndex());
129 gatherIndexTree(indices, editor, accessor.getTypeIndex());
130 }
131
132 @Override
133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
137 }
138
139 @Override
140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
145 }
146 },
147 MethodHandleInfo(15) {
148 @Override
149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
151 gatherIndexTree(indices, editor, accessor.getTypeIndex());
152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex());
153 }
154
155 @Override
156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex()));
160 }
161
162 @Override
163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex());
167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
168 }
169 },
170 MethodTypeInfo(16) {
171 @Override
172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
174 gatherIndexTree(indices, editor, accessor.getTypeIndex());
175 }
176
177 @Override
178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
181 }
182
183 @Override
184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
188 }
189 },
190 InvokeDynamicInfo(18) {
191 @Override
192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex());
195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
196 }
197
198 @Override
199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex()));
202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
203 }
204
205 @Override
206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex());
209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
211 }
212 };
213
214 private static Map<Integer, InfoType> types;
215
216 static {
217 types = Maps.newTreeMap();
218 for (InfoType type : values()) {
219 types.put(type.getTag(), type);
220 }
221 }
222
223 private int tag;
224
225 InfoType(int tag) {
226 this.tag = tag;
227 }
228
229 public static InfoType getByTag(int tag) {
230 return types.get(tag);
231 }
232
233 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) {
234 // add own index
235 indices.add(index);
236
237 // recurse
238 ConstInfoAccessor entry = editor.getItem(index);
239 entry.getType().gatherIndexTree(indices, editor, entry);
240 }
241
242 private static int remapIndex(Map<Integer, Integer> map, int index) {
243 Integer newIndex = map.get(index);
244 if (newIndex == null) {
245 newIndex = index;
246 }
247 return newIndex;
248 }
249
250 public int getTag() {
251 return this.tag;
252 }
253
254 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
255 // by default, do nothing
256 }
257
258 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
259 // by default, do nothing
260 }
261
262 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
263 // by default, everything is good
264 return true;
265 }
266}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
deleted file mode 100644
index 57d60fd..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
+++ /dev/null
@@ -1,85 +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 javassist.bytecode.AttributeInfo;
15import javassist.bytecode.ConstPool;
16import javassist.bytecode.MethodInfo;
17
18import java.io.ByteArrayOutputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.List;
23
24public class MethodParametersAttribute extends AttributeInfo {
25
26 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) {
27 super(pool, "MethodParameters", writeStruct(parameterNameIndices));
28 }
29
30 public static void updateClass(MethodInfo info, List<String> names) {
31
32 // add the names to the class const pool
33 ConstPool constPool = info.getConstPool();
34 List<Integer> parameterNameIndices = new ArrayList<>();
35 for (String name : names) {
36 if (name != null) {
37 parameterNameIndices.add(constPool.addUtf8Info(name));
38 }
39 }
40
41 // add the attribute to the method
42 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices));
43 }
44
45 private static byte[] writeStruct(List<Integer> parameterNameIndices) {
46 // JVM 8 Spec says the struct looks like this:
47 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24
48 // uint8 num_params
49 // for each param:
50 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
51 // uint16 access_flags -> don't care, just set to 0
52
53 ByteArrayOutputStream buf = new ByteArrayOutputStream();
54 DataOutputStream out = new DataOutputStream(buf);
55
56 // NOTE: java hates unsigned integers, so we have to be careful here
57 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
58 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
59 // if the int is out of range, the byte stream won't look the way we want and weird things will happen
60 final int SIZEOF_UINT8 = 1;
61 final int SIZEOF_UINT16 = 2;
62 final int MAX_UINT8 = (1 << 8) - 1;
63 final int MAX_UINT16 = (1 << 16) - 1;
64
65 try {
66 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8);
67 out.writeByte(parameterNameIndices.size());
68
69 for (Integer index : parameterNameIndices) {
70 assert (index >= 0 && index <= MAX_UINT16);
71 out.writeShort(index);
72
73 // just write 0 for the access flags
74 out.writeShort(0);
75 }
76
77 out.close();
78 byte[] data = buf.toByteArray();
79 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16));
80 return data;
81 } catch (IOException ex) {
82 throw new Error(ex);
83 }
84 }
85}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
deleted file mode 100644
index eaa6e90..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
+++ /dev/null
@@ -1,56 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class ClassInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field nameIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.ClassInfo");
24 nameIndex = clazz.getDeclaredField("name");
25 nameIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public ClassInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getNameIndex() {
42 try {
43 return (Integer) nameIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setNameIndex(int val) {
50 try {
51 nameIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
deleted file mode 100644
index 27d991a..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
+++ /dev/null
@@ -1,124 +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.accessors;
13
14import com.google.common.base.Charsets;
15import cuchaz.enigma.bytecode.InfoType;
16
17import java.io.*;
18import java.lang.reflect.Field;
19import java.lang.reflect.Method;
20
21public class ConstInfoAccessor {
22
23 private static Class<?> clazz;
24 private static Field index;
25 private static Method getTag;
26
27 static {
28 try {
29 clazz = Class.forName("javassist.bytecode.ConstInfo");
30 index = clazz.getDeclaredField("index");
31 index.setAccessible(true);
32 getTag = clazz.getMethod("getTag");
33 getTag.setAccessible(true);
34 } catch (Exception ex) {
35 throw new Error(ex);
36 }
37 }
38
39 private Object item;
40
41 public ConstInfoAccessor(Object item) {
42 if (item == null) {
43 throw new IllegalArgumentException("item cannot be null!");
44 }
45 this.item = item;
46 }
47
48 public Object getItem() {
49 return this.item;
50 }
51
52 public int getIndex() {
53 try {
54 return (Integer) index.get(this.item);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getTag() {
61 try {
62 return (Integer) getTag.invoke(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public ConstInfoAccessor copy() {
69 return new ConstInfoAccessor(copyItem());
70 }
71
72 public Object copyItem() {
73 // I don't know of a simpler way to copy one of these silly things...
74 try {
75 // serialize the item
76 ByteArrayOutputStream buf = new ByteArrayOutputStream();
77 DataOutputStream out = new DataOutputStream(buf);
78 write(out);
79
80 // deserialize the item
81 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray()));
82 Object item = new ConstInfoAccessor(in).getItem();
83 in.close();
84
85 return item;
86 } catch (Exception ex) {
87 throw new Error(ex);
88 }
89 }
90
91 public void write(DataOutputStream out) throws IOException {
92 try {
93 out.writeUTF(this.item.getClass().getName());
94 out.writeInt(getIndex());
95
96 Method method = this.item.getClass().getMethod("write", DataOutputStream.class);
97 method.setAccessible(true);
98 method.invoke(this.item, out);
99 } catch (IOException ex) {
100 throw ex;
101 } catch (Exception ex) {
102 throw new Error(ex);
103 }
104 }
105
106 @Override
107 public String toString() {
108 try {
109 ByteArrayOutputStream buf = new ByteArrayOutputStream();
110 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8));
111 Method print = this.item.getClass().getMethod("print", PrintWriter.class);
112 print.setAccessible(true);
113 print.invoke(this.item, out);
114 out.close();
115 return buf.toString("UTF-8").replace("\n", "");
116 } catch (Exception ex) {
117 throw new Error(ex);
118 }
119 }
120
121 public InfoType getType() {
122 return InfoType.getByTag(getTag());
123 }
124}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
deleted file mode 100644
index aef3532..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
+++ /dev/null
@@ -1,75 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class InvokeDynamicInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field bootstrapIndex;
20 private static Field nameAndTypeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo");
25 bootstrapIndex = clazz.getDeclaredField("bootstrap");
26 bootstrapIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndType");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public InvokeDynamicInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getBootstrapIndex() {
45 try {
46 return (Integer) bootstrapIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setBootstrapIndex(int val) {
53 try {
54 bootstrapIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getNameAndTypeIndex() {
61 try {
62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setNameAndTypeIndex(int val) {
69 try {
70 nameAndTypeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
deleted file mode 100644
index 058bb45..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
+++ /dev/null
@@ -1,75 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class MemberRefInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field classIndex;
20 private static Field nameAndTypeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MemberrefInfo");
25 classIndex = clazz.getDeclaredField("classIndex");
26 classIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public MemberRefInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getClassIndex() {
45 try {
46 return (Integer) classIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setClassIndex(int val) {
53 try {
54 classIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getNameAndTypeIndex() {
61 try {
62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setNameAndTypeIndex(int val) {
69 try {
70 nameAndTypeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
deleted file mode 100644
index 985e792..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
+++ /dev/null
@@ -1,75 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class MethodHandleInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field kindIndex;
20 private static Field indexIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MethodHandleInfo");
25 kindIndex = clazz.getDeclaredField("refKind");
26 kindIndex.setAccessible(true);
27 indexIndex = clazz.getDeclaredField("refIndex");
28 indexIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public MethodHandleInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getTypeIndex() {
45 try {
46 return (Integer) kindIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setTypeIndex(int val) {
53 try {
54 kindIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getMethodRefIndex() {
61 try {
62 return (Integer) indexIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setMethodRefIndex(int val) {
69 try {
70 indexIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
deleted file mode 100644
index 10b0cb0..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
+++ /dev/null
@@ -1,57 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class MethodTypeInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field descriptorIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.MethodTypeInfo");
24 descriptorIndex = clazz.getDeclaredField("descriptor");
25 descriptorIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public MethodTypeInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getTypeIndex() {
42 try {
43 return (Integer) descriptorIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setTypeIndex(int val) {
50 try {
51 descriptorIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56
57}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
deleted file mode 100644
index cc7fdbe..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
+++ /dev/null
@@ -1,75 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class NameAndTypeInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field nameIndex;
20 private static Field typeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.NameAndTypeInfo");
25 nameIndex = clazz.getDeclaredField("memberName");
26 nameIndex.setAccessible(true);
27 typeIndex = clazz.getDeclaredField("typeDescriptor");
28 typeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public NameAndTypeInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getNameIndex() {
45 try {
46 return (Integer) nameIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setNameIndex(int val) {
53 try {
54 nameIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getTypeIndex() {
61 try {
62 return (Integer) typeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setTypeIndex(int val) {
69 try {
70 typeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
deleted file mode 100644
index 5c68d4a..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
+++ /dev/null
@@ -1,56 +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.accessors;
13
14import java.lang.reflect.Field;
15
16public class StringInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field stringIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.StringInfo");
24 stringIndex = clazz.getDeclaredField("string");
25 stringIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public StringInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getStringIndex() {
42 try {
43 return (Integer) stringIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setStringIndex(int val) {
50 try {
51 stringIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
deleted file mode 100644
index cc3b41b..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
+++ /dev/null
@@ -1,29 +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.accessors;
13
14public class Utf8InfoAccessor {
15
16 private static Class<?> clazz;
17
18 static {
19 try {
20 clazz = Class.forName("javassist.bytecode.Utf8Info");
21 } catch (Exception ex) {
22 throw new Error(ex);
23 }
24 }
25
26 public static boolean isType(ConstInfoAccessor accessor) {
27 return clazz.isAssignableFrom(accessor.getItem().getClass());
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java
deleted file mode 100644
index 4ac5a8b..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java
+++ /dev/null
@@ -1,161 +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.translators;
13
14import cuchaz.enigma.bytecode.ClassRenamer;
15import cuchaz.enigma.bytecode.ConstPoolEditor;
16import cuchaz.enigma.mapping.*;
17import javassist.CtBehavior;
18import javassist.CtClass;
19import javassist.CtField;
20import javassist.CtMethod;
21import javassist.bytecode.*;
22
23public class ClassTranslator {
24
25 public static void translate(Translator translator, CtClass c) {
26
27 // NOTE: the order of these translations is very important
28
29 // translate all the field and method references in the code by editing the constant pool
30 ConstPool constants = c.getClassFile().getConstPool();
31 ConstPoolEditor editor = new ConstPoolEditor(constants);
32 for (int i = 1; i < constants.getSize(); i++) {
33 switch (constants.getTag(i)) {
34
35 case ConstPool.CONST_Fieldref: {
36
37 // translate the name and type
38 FieldEntry entry = EntryFactory.getFieldEntry(
39 Descriptor.toJvmName(constants.getFieldrefClassName(i)),
40 constants.getFieldrefName(i),
41 constants.getFieldrefType(i)
42 );
43 FieldEntry translatedEntry = translator.translateEntry(entry);
44 if (!entry.equals(translatedEntry)) {
45 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString());
46 }
47 }
48 break;
49
50 case ConstPool.CONST_Methodref:
51 case ConstPool.CONST_InterfaceMethodref: {
52
53 // translate the name and type (ie signature)
54 BehaviorEntry entry = EntryFactory.getBehaviorEntry(
55 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
56 editor.getMemberrefName(i),
57 editor.getMemberrefType(i)
58 );
59 BehaviorEntry translatedEntry = translator.translateEntry(entry);
60 if (!entry.equals(translatedEntry)) {
61 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString());
62 }
63 }
64 break;
65 default:
66 break;
67 }
68 }
69
70 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
71 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
72 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
73 ClassRenamer.applyModifier(c, modifier);
74
75 // translate all the fields
76 for (CtField field : c.getDeclaredFields()) {
77
78 // translate the name
79 FieldEntry entry = EntryFactory.getFieldEntry(field);
80 String translatedName = translator.translate(entry);
81 modifier = translator.getModifier(entry);
82 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
83 ClassRenamer.applyModifier(field, modifier);
84
85 if (translatedName != null) {
86 field.setName(translatedName);
87 }
88
89 // translate the type
90 Type translatedType = translator.translateType(entry.getType());
91 field.getFieldInfo().setDescriptor(translatedType.toString());
92 }
93
94 // translate all the methods and constructors
95 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
96
97 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
98
99 modifier = translator.getModifier(entry);
100 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
101 ClassRenamer.applyModifier(behavior, modifier);
102
103 if (behavior instanceof CtMethod) {
104 CtMethod method = (CtMethod) behavior;
105
106 // translate the name
107 String translatedName = translator.translate(entry);
108 if (translatedName != null) {
109 method.setName(translatedName);
110 }
111 }
112
113 if (entry.getSignature() != null) {
114 // translate the signature
115 Signature translatedSignature = translator.translateSignature(entry.getSignature());
116 behavior.getMethodInfo().setDescriptor(translatedSignature.toString());
117 }
118 }
119
120 // translate the EnclosingMethod attribute
121 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
122 if (enclosingMethodAttr != null) {
123
124 if (enclosingMethodAttr.methodIndex() == 0) {
125 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className()));
126 BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
127 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
128 constants,
129 deobfBehaviorEntry.getClassName()
130 ));
131 } else {
132 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(
133 Descriptor.toJvmName(enclosingMethodAttr.className()),
134 enclosingMethodAttr.methodName(),
135 enclosingMethodAttr.methodDescriptor()
136 );
137 BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
138 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
139 constants,
140 deobfBehaviorEntry.getClassName(),
141 deobfBehaviorEntry.getName(),
142 deobfBehaviorEntry.getSignature().toString()
143 ));
144 }
145 }
146
147 // translate all the class names referenced in the code
148 // the above code only changed method/field/reference names and types, but not the rest of the class references
149 ClassRenamer.renameClasses(c, translator);
150
151 // translate the source file attribute too
152 ClassEntry deobfClassEntry = translator.translateEntry(classEntry);
153 if (deobfClassEntry != null) {
154 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
155 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
156 }
157 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
158 if (attr != null)
159 InnerClassWriter.changeModifier(c, attr, translator);
160 }
161}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
deleted file mode 100644
index 0e35938..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
+++ /dev/null
@@ -1,144 +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.translators;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.bytecode.ClassRenamer;
17import cuchaz.enigma.mapping.*;
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.*;
22
23import java.util.Collection;
24import java.util.List;
25
26public class InnerClassWriter {
27
28 // FIXME: modifier is not applied to inner class
29 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
30 ClassPool pool = c.getClassPool();
31 for (int i = 0; i < attr.tableLength(); i++) {
32
33 String innerName = attr.innerClass(i);
34 // get the inner class full name (which has already been translated)
35 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
36 try {
37 CtClass innerClass = pool.get(innerName);
38 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
39 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
40 ClassRenamer.applyModifier(innerClass, modifier);
41 } catch (NotFoundException e) {
42 // This shouldn't be possible in theory
43 //e.printStackTrace();
44 }
45 }
46 }
47
48 public static void write(JarIndex index, CtClass c) {
49
50 // don't change anything if there's already an attribute there
51 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
52 if (oldAttr != null) {
53 // bail!
54 return;
55 }
56
57 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
58 List<ClassEntry> obfClassChain = index.getObfClassChain(obfClassEntry);
59
60 boolean isInnerClass = obfClassChain.size() > 1;
61 if (isInnerClass) {
62
63 // it's an inner class, rename it to the fully qualified name
64 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName());
65
66 BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry);
67 if (caller != null) {
68
69 // write the enclosing method attribute
70 if (caller.getName().equals("<clinit>")) {
71 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName()));
72 } else {
73 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString()));
74 }
75 }
76 }
77
78 // does this class have any inner classes?
79 Collection<ClassEntry> obfInnerClassEntries = index.getInnerClasses(obfClassEntry);
80
81 if (isInnerClass || !obfInnerClassEntries.isEmpty()) {
82
83 // create an inner class attribute
84 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool());
85 c.getClassFile().addAttribute(attr);
86
87 // write the ancestry, but not the outermost class
88 for (int i = 1; i < obfClassChain.size(); i++) {
89 ClassEntry obfInnerClassEntry = obfClassChain.get(i);
90 writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry);
91
92 // update references to use the fully qualified inner class name
93 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName());
94 }
95
96 // write the inner classes
97 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) {
98
99 // extend the class chain
100 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain);
101 extendedObfClassChain.add(obfInnerClassEntry);
102
103 writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry);
104
105 // update references to use the fully qualified inner class name
106 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName());
107 }
108 }
109 }
110
111 private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
112
113 // get the new inner class name
114 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain);
115 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry();
116
117 // here's what the JVM spec says about the InnerClasses attribute
118 // append(inner, parent, 0 if anonymous else simple name, flags);
119
120 // update the attribute with this inner class
121 ConstPool constPool = attr.getConstPool();
122 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName());
123 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName());
124 int innerClassNameIndex = 0;
125 int accessFlags = AccessFlag.PUBLIC;
126 // TODO: need to figure out if we can put static or not
127 if (!index.isAnonymousClass(obfClassEntry)) {
128 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName());
129 }
130
131 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
132
133 /* DEBUG
134 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)",
135 obfClassEntry,
136 attr.innerClass(attr.tableLength() - 1),
137 attr.outerClass(attr.tableLength() - 1),
138 attr.innerName(attr.tableLength() - 1),
139 Constants.NonePackage + "/" + obfInnerClassName,
140 obfClassEntry.getName()
141 ));
142 */
143 }
144}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java
deleted file mode 100644
index 51b3d2d..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java
+++ /dev/null
@@ -1,142 +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.translators;
13
14import cuchaz.enigma.mapping.*;
15import javassist.CtBehavior;
16import javassist.CtClass;
17import javassist.bytecode.*;
18
19public class LocalVariableTranslator {
20
21 public static void translate(Translator translator, CtClass c) {
22 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
23
24 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
25 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
26 if (codeAttribute == null) {
27 continue;
28 }
29
30 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
31 ConstPool constants = c.getClassFile().getConstPool();
32
33 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
34 if (table != null) {
35 renameLVT(translator, behaviorEntry, constants, table, c);
36 }
37
38 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
39 if (typeTable != null) {
40 renameLVTT(typeTable, table);
41 }
42 }
43 }
44
45 // DEBUG
46 @SuppressWarnings("unused")
47 private static void dumpTable(LocalVariableAttribute table) {
48 for (int i = 0; i < table.tableLength(); i++) {
49 System.out.println(String.format("\t%d (%d): %s %s",
50 i, table.index(i), table.variableName(i), table.descriptor(i)
51 ));
52 }
53 }
54
55 private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) {
56
57 // skip empty tables
58 if (table.tableLength() <= 0) {
59 return;
60 }
61
62 // where do we start counting variables?
63 int starti = 0;
64 if (table.variableName(0).equals("this")) {
65 // skip the "this" variable
66 starti++;
67 }
68
69 // rename method arguments first
70 int numArgs = 0;
71 if (behaviorEntry.getSignature() != null) {
72 numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
73 boolean isNestedClassConstructor = false;
74
75 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
76 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
77 // Get the first arg type
78 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
79
80 // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
81 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
82 isNestedClassConstructor = true;
83 numArgs--;
84 }
85 }
86
87 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
88 int argi = i - starti;
89 if (ctClass.isEnum())
90 argi += 2;
91 String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
92 if (argName == null) {
93 int argIndex = isNestedClassConstructor ? argi + 1 : argi;
94 if (ctClass.isEnum())
95 argIndex -= 2;
96 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex);
97 // Unfortunately each of these have different name getters, so they have different code paths
98 if (argType.isPrimitive()) {
99 Type.Primitive argCls = argType.getPrimitive();
100 argName = "a" + argCls.name() + (argIndex + 1);
101 } else if (argType.isArray()) {
102 // List types would require this whole block again, so just go with aListx
103 argName = "aList" + (argIndex + 1);
104 } else if (argType.isClass()) {
105 ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry());
106 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1);
107 } else {
108 argName = "a" + (argIndex + 1);
109 }
110 }
111 renameVariable(table, i, constants.addUtf8Info(argName));
112 }
113 }
114
115 // then rename the rest of the args, if any
116 for (int i = starti + numArgs; i < table.tableLength(); i++) {
117 int firstIndex = Math.min(table.index(starti + numArgs), table.index(i));
118 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
119 }
120 }
121
122 private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
123 // rename args to the same names as in the LVT
124 for (int i = 0; i < typeTable.tableLength(); i++) {
125 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
126 }
127 }
128
129 private static void renameVariable(LocalVariableAttribute table, int i, int stringId) {
130 // based off of LocalVariableAttribute.nameIndex()
131 ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
132 }
133
134 private static int getNameIndex(LocalVariableAttribute table, int index) {
135 for (int i = 0; i < table.tableLength(); i++) {
136 if (table.index(i) == index) {
137 return table.nameIndex(i);
138 }
139 }
140 return 0;
141 }
142}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
deleted file mode 100644
index 4e632b9..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
+++ /dev/null
@@ -1,62 +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.translators;
13
14import cuchaz.enigma.bytecode.MethodParametersAttribute;
15import cuchaz.enigma.mapping.*;
16import javassist.CtBehavior;
17import javassist.CtClass;
18import javassist.bytecode.CodeAttribute;
19import javassist.bytecode.LocalVariableAttribute;
20
21import java.util.ArrayList;
22import java.util.List;
23
24public class MethodParameterTranslator {
25
26 public static void translate(Translator translator, CtClass c) {
27
28 // Procyon will read method arguments from the "MethodParameters" attribute, so write those
29 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
30
31 // if there's a local variable table here, don't write a MethodParameters attribute
32 // let the local variable writer deal with it instead
33 // procyon starts doing really weird things if we give it both attributes
34 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
35 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
36 continue;
37 }
38
39 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
40
41 // get the number of arguments
42 Signature signature = behaviorEntry.getSignature();
43 if (signature == null) {
44 // static initializers have no signatures, or arguments
45 continue;
46 }
47 int numParams = signature.getArgumentTypes().size();
48 if (numParams <= 0) {
49 continue;
50 }
51
52 // get the list of argument names
53 List<String> names = new ArrayList<>(numParams);
54 for (int i = 0; i < numParams; i++) {
55 names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, "")));
56 }
57
58 // save the mappings to the class
59 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names);
60 }
61 }
62}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
new file mode 100644
index 0000000..df5f8f7
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
@@ -0,0 +1,43 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.ClassEntry;
6import cuchaz.enigma.mapping.entry.FieldEntry;
7import org.objectweb.asm.AnnotationVisitor;
8
9public class TranslationAnnotationVisitor extends AnnotationVisitor {
10 private final Translator translator;
11 private final ClassEntry annotationEntry;
12
13 public TranslationAnnotationVisitor(Translator translator, ClassEntry annotationEntry, int api, AnnotationVisitor av) {
14 super(api, av);
15 this.translator = translator;
16 this.annotationEntry = annotationEntry;
17 }
18
19 @Override
20 public void visit(String name, Object value) {
21 super.visit(name, translator.getTranslatedValue(value));
22 }
23
24 @Override
25 public AnnotationVisitor visitArray(String name) {
26 return this;
27 }
28
29 @Override
30 public AnnotationVisitor visitAnnotation(String name, String desc) {
31 TypeDescriptor type = new TypeDescriptor(desc);
32 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type));
33 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString());
34 }
35
36 @Override
37 public void visitEnum(String name, String desc, String value) {
38 TypeDescriptor type = new TypeDescriptor(desc);
39 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type));
40 FieldEntry enumField = translator.getTranslatedField(new FieldEntry(type.getTypeEntry(), value, type));
41 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName());
42 }
43}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
new file mode 100644
index 0000000..234d11f
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -0,0 +1,113 @@
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.translators;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.MethodDescriptor;
17import cuchaz.enigma.mapping.Signature;
18import cuchaz.enigma.mapping.Translator;
19import cuchaz.enigma.mapping.TypeDescriptor;
20import cuchaz.enigma.mapping.entry.*;
21import org.objectweb.asm.*;
22
23public class TranslationClassVisitor extends ClassVisitor {
24 private final Translator translator;
25 private final JarIndex jarIndex;
26 private final ReferencedEntryPool entryPool;
27
28 private ClassDefEntry obfClassEntry;
29 private Signature obfSignature;
30
31 public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) {
32 super(api, cv);
33 this.translator = translator;
34 this.jarIndex = jarIndex;
35 this.entryPool = entryPool;
36 }
37
38 @Override
39 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
40 obfSignature = Signature.createSignature(signature);
41 obfClassEntry = new ClassDefEntry(name, obfSignature, new AccessFlags(access));
42 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(obfClassEntry);
43 ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName));
44 String[] translatedInterfaces = new String[interfaces.length];
45 for (int i = 0; i < interfaces.length; i++) {
46 translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName();
47 }
48 super.visit(version, translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getSignature().toString(), superEntry.getName(), translatedInterfaces);
49 }
50
51 @Override
52 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
53 FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
54 FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry);
55 FieldVisitor fv = super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), value);
56 return new TranslationFieldVisitor(translator, translatedEntry, api, fv);
57 }
58
59 @Override
60 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
61 MethodDefEntry entry = new MethodDefEntry(obfClassEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
62 MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry);
63 if (jarIndex.getBridgedMethod(entry) != null) {
64 translatedEntry.getAccess().setBridged();
65 }
66 String[] translatedExceptions = new String[exceptions.length];
67 for (int i = 0; i < exceptions.length; i++) {
68 translatedExceptions[i] = translator.getTranslatedClass(entryPool.getClass(exceptions[i])).getName();
69 }
70 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions);
71 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv);
72 }
73
74 @Override
75 public void visitInnerClass(String name, String outerName, String innerName, int access) {
76 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(name, obfSignature, new AccessFlags(access)));
77 String translatedName = translatedEntry.getName();
78 int separatorIndex = translatedName.lastIndexOf("$");
79 String parentName = translatedName.substring(0, separatorIndex);
80 String childName = translatedName.substring(separatorIndex + 1);
81
82 ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(parentName));
83
84 // Anonymous classes do not specify an outer or inner name. As we do not translate from the given parameter, ignore if the input is null
85 String translatedOuterName = outerName != null ? outerEntry.getName() : null;
86 String translatedInnerName = innerName != null ? childName : null;
87 super.visitInnerClass(translatedName, translatedOuterName, translatedInnerName, translatedEntry.getAccess().getFlags());
88 }
89
90 @Override
91 public void visitOuterClass(String owner, String name, String desc) {
92 if (desc != null) {
93 MethodEntry translatedEntry = translator.getTranslatedMethod(new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)));
94 super.visitOuterClass(translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
95 } else {
96 super.visitOuterClass(owner, name, desc);
97 }
98 }
99
100 @Override
101 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
102 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
103 AnnotationVisitor av = super.visitAnnotation(translatedDesc.toString(), visible);
104 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
105 }
106
107 @Override
108 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
109 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
110 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible);
111 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
new file mode 100644
index 0000000..e4695fb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
@@ -0,0 +1,33 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.FieldDefEntry;
6import org.objectweb.asm.AnnotationVisitor;
7import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.TypePath;
9
10public class TranslationFieldVisitor extends FieldVisitor {
11 private final FieldDefEntry fieldEntry;
12 private final Translator translator;
13
14 public TranslationFieldVisitor(Translator translator, FieldDefEntry fieldEntry, int api, FieldVisitor fv) {
15 super(api, fv);
16 this.translator = translator;
17 this.fieldEntry = fieldEntry;
18 }
19
20 @Override
21 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
22 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
23 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
24 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
25 }
26
27 @Override
28 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
29 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
30 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
31 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
32 }
33}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
new file mode 100644
index 0000000..0141b45
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -0,0 +1,191 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.MethodDescriptor;
4import cuchaz.enigma.mapping.Signature;
5import cuchaz.enigma.mapping.Translator;
6import cuchaz.enigma.mapping.TypeDescriptor;
7import cuchaz.enigma.mapping.entry.*;
8import org.objectweb.asm.*;
9
10import java.util.List;
11import java.util.Locale;
12
13public class TranslationMethodVisitor extends MethodVisitor {
14 private final ClassDefEntry ownerEntry;
15 private final MethodDefEntry methodEntry;
16 private final Translator translator;
17
18 private boolean hasParameterMeta;
19
20 public TranslationMethodVisitor(Translator translator, ClassDefEntry ownerEntry, MethodDefEntry methodEntry, int api, MethodVisitor mv) {
21 super(api, mv);
22 this.translator = translator;
23 this.ownerEntry = ownerEntry;
24 this.methodEntry = methodEntry;
25 }
26
27 @Override
28 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
29 FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc));
30 FieldEntry translatedEntry = translator.getTranslatedField(entry);
31 super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
32 }
33
34 @Override
35 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
36 MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc));
37 MethodEntry translatedEntry = translator.getTranslatedMethod(entry);
38 super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf);
39 }
40
41 @Override
42 public void visitFrame(int type, int localCount, Object[] locals, int stackCount, Object[] stack) {
43 Object[] translatedLocals = this.getTranslatedFrame(locals, localCount);
44 Object[] translatedStack = this.getTranslatedFrame(stack, stackCount);
45 super.visitFrame(type, localCount, translatedLocals, stackCount, translatedStack);
46 }
47
48 private Object[] getTranslatedFrame(Object[] array, int count) {
49 if (array == null) {
50 return null;
51 }
52 for (int i = 0; i < count; i++) {
53 Object object = array[i];
54 if (object instanceof String) {
55 String type = (String) object;
56 array[i] = translator.getTranslatedClass(new ClassEntry(type)).getName();
57 }
58 }
59 return array;
60 }
61
62 @Override
63 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
64 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
65 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
66 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
67 }
68
69 @Override
70 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
71 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
72 AnnotationVisitor av = super.visitParameterAnnotation(parameter, typeDesc.toString(), visible);
73 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
74 }
75
76 @Override
77 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
78 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
79 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, typeDesc.toString(), visible);
80 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
81 }
82
83 @Override
84 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
85 hasParameterMeta = true;
86
87 String translatedSignature = translator.getTranslatedSignature(Signature.createTypedSignature(signature)).toString();
88
89 int offset = methodEntry.getVariableOffset(ownerEntry);
90
91 int offsetIndex = index - offset;
92 if (offsetIndex >= 0) {
93 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, offsetIndex, name, new TypeDescriptor(desc));
94 LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry);
95 String translatedName = translatedEntry.getName();
96
97 // TODO: Better name inference
98 if (translatedName.equals(entry.getName())) {
99 boolean argument = offsetIndex < methodEntry.getDesc().getArgumentDescs().size();
100 translatedName = inferName(argument, offsetIndex, translatedEntry.getDesc());
101 }
102
103 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index);
104 } else {
105 // Handle "this" variable
106 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
107 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index);
108 }
109 }
110
111 @Override
112 public void visitTypeInsn(int opcode, String type) {
113 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type));
114 super.visitTypeInsn(opcode, translatedEntry.getName());
115 }
116
117 @Override
118 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
119 MethodDescriptor translatedMethodDesc = translator.getTranslatedMethodDesc(new MethodDescriptor(desc));
120 Object[] translatedBsmArgs = new Object[bsmArgs.length];
121 for (int i = 0; i < bsmArgs.length; i++) {
122 translatedBsmArgs[i] = translator.getTranslatedValue(bsmArgs[i]);
123 }
124 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), translator.getTranslatedHandle(bsm), translatedBsmArgs);
125 }
126
127 @Override
128 public void visitLdcInsn(Object cst) {
129 super.visitLdcInsn(translator.getTranslatedValue(cst));
130 }
131
132 @Override
133 public void visitMultiANewArrayInsn(String desc, int dims) {
134 super.visitMultiANewArrayInsn(translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString(), dims);
135 }
136
137 @Override
138 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
139 if (type != null) {
140 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type));
141 super.visitTryCatchBlock(start, end, handler, translatedEntry.getName());
142 } else {
143 super.visitTryCatchBlock(start, end, handler, type);
144 }
145 }
146
147 @Override
148 public void visitEnd() {
149 // If we didn't receive any parameter metadata, generate it
150 if (!hasParameterMeta) {
151 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
152 for (int index = 0; index < arguments.size(); index++) {
153 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "");
154 LocalVariableEntry translatedEntry = translator.getTranslatedVariable(entry);
155 String translatedName = translatedEntry.getName();
156 if (translatedName.equals(entry.getName())) {
157 super.visitParameter(inferName(true, index, arguments.get(index)), 0);
158 } else {
159 super.visitParameter(translatedName, 0);
160 }
161 }
162 }
163 super.visitEnd();
164 }
165
166 private String inferName(boolean argument, int argumentIndex, TypeDescriptor desc) {
167 String translatedName;
168 int nameIndex = argumentIndex + 1;
169 StringBuilder nameBuilder = new StringBuilder(argument ? "a" : "v");
170 // Unfortunately each of these have different name getters, so they have different code paths
171 if (desc.isPrimitive()) {
172 TypeDescriptor.Primitive argCls = desc.getPrimitive();
173 nameBuilder.append(argCls.name());
174 } else if (desc.isArray()) {
175 // List types would require this whole block again, so just go with aListx
176 nameBuilder.append("Arr");
177 } else if (desc.isType()) {
178 String typeName = desc.getTypeEntry().getSimpleName().replace("$", "");
179 typeName = typeName.substring(0, 1).toUpperCase(Locale.ROOT) + typeName.substring(1);
180 nameBuilder.append(typeName);
181 } else {
182 System.err.println("Encountered invalid argument type descriptor " + desc.toString());
183 nameBuilder.append("Unk");
184 }
185 if (!argument || methodEntry.getDesc().getArgumentDescs().size() > 1) {
186 nameBuilder.append(nameIndex);
187 }
188 translatedName = nameBuilder.toString();
189 return translatedName;
190 }
191}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
new file mode 100644
index 0000000..e66b085
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
@@ -0,0 +1,129 @@
1package cuchaz.enigma.bytecode.translators;
2
3import org.objectweb.asm.Opcodes;
4import org.objectweb.asm.signature.SignatureVisitor;
5
6import java.util.Stack;
7import java.util.function.Function;
8
9public class TranslationSignatureVisitor extends SignatureVisitor {
10 private final Function<String, String> remapper;
11
12 private final SignatureVisitor sv;
13 private final Stack<String> classStack = new Stack<>();
14
15 public TranslationSignatureVisitor(Function<String, String> remapper, SignatureVisitor sv) {
16 super(Opcodes.ASM5);
17 this.remapper = remapper;
18 this.sv = sv;
19 }
20
21 @Override
22 public void visitClassType(String name) {
23 classStack.push(name);
24 String translatedEntry = this.remapper.apply(name);
25 this.sv.visitClassType(translatedEntry);
26 }
27
28 @Override
29 public void visitInnerClassType(String name) {
30 String lastClass = classStack.pop();
31 if (!name.startsWith(lastClass+"$")){//todo see if there's a way to base this on whether there were type params or not
32 name = lastClass+"$"+name;
33 }
34 String translatedEntry = this.remapper.apply(name);
35 if (translatedEntry.contains("/")){
36 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/")+1);
37 }
38 if (translatedEntry.contains("$")){
39 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$")+1);
40 }
41 this.sv.visitInnerClassType(translatedEntry);
42 }
43
44 @Override
45 public void visitFormalTypeParameter(String name) {
46 this.sv.visitFormalTypeParameter(name);
47 }
48
49 @Override
50 public void visitTypeVariable(String name) {
51 this.sv.visitTypeVariable(name);
52 }
53
54 @Override
55 public SignatureVisitor visitArrayType() {
56 this.sv.visitArrayType();
57 return this;
58 }
59
60 @Override
61 public void visitBaseType(char descriptor) {
62 this.sv.visitBaseType(descriptor);
63 }
64
65 @Override
66 public SignatureVisitor visitClassBound() {
67 this.sv.visitClassBound();
68 return this;
69 }
70
71 @Override
72 public SignatureVisitor visitExceptionType() {
73 this.sv.visitExceptionType();
74 return this;
75 }
76
77 @Override
78 public SignatureVisitor visitInterface() {
79 this.sv.visitInterface();
80 return this;
81 }
82
83 @Override
84 public SignatureVisitor visitInterfaceBound() {
85 this.sv.visitInterfaceBound();
86 return this;
87 }
88
89 @Override
90 public SignatureVisitor visitParameterType() {
91 this.sv.visitParameterType();
92 return this;
93 }
94
95 @Override
96 public SignatureVisitor visitReturnType() {
97 this.sv.visitReturnType();
98 return this;
99 }
100
101 @Override
102 public SignatureVisitor visitSuperclass() {
103 this.sv.visitSuperclass();
104 return this;
105 }
106
107 @Override
108 public void visitTypeArgument() {
109 this.sv.visitTypeArgument();
110 }
111
112 @Override
113 public SignatureVisitor visitTypeArgument(char wildcard) {
114 this.sv.visitTypeArgument(wildcard);
115 return this;
116 }
117
118 @Override
119 public void visitEnd() {
120 this.sv.visitEnd();
121 if (!classStack.empty())
122 classStack.pop();
123 }
124
125 @Override
126 public String toString() {
127 return this.sv.toString();
128 }
129}