summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/bytecode
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/bytecode')
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java60
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java60
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java1028
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java291
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java483
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InfoType.java492
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java239
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java249
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java111
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java217
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java100
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java99
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java77
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java23
19 files changed, 1980 insertions, 1976 deletions
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
index ad5bab0..6ec576e 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import javassist.CtBehavior; 14import javassist.CtBehavior;
@@ -16,36 +17,35 @@ import javassist.CtField;
16import javassist.bytecode.AccessFlag; 17import javassist.bytecode.AccessFlag;
17import javassist.bytecode.InnerClassesAttribute; 18import javassist.bytecode.InnerClassesAttribute;
18 19
19
20public class ClassProtectifier { 20public class ClassProtectifier {
21 21
22 public static CtClass protectify(CtClass c) { 22 public static CtClass protectify(CtClass c) {
23 23
24 // protectify all the fields 24 // protectify all the fields
25 for (CtField field : c.getDeclaredFields()) { 25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(protectify(field.getModifiers())); 26 field.setModifiers(protectify(field.getModifiers()));
27 } 27 }
28 28
29 // protectify all the methods and constructors 29 // protectify all the methods and constructors
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 30 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
31 behavior.setModifiers(protectify(behavior.getModifiers())); 31 behavior.setModifiers(protectify(behavior.getModifiers()));
32 } 32 }
33 33
34 // protectify all the inner classes 34 // protectify all the inner classes
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
36 if (attr != null) { 36 if (attr != null) {
37 for (int i = 0; i < attr.tableLength(); i++) { 37 for (int i = 0; i < attr.tableLength(); i++) {
38 attr.setAccessFlags(i, protectify(attr.accessFlags(i))); 38 attr.setAccessFlags(i, protectify(attr.accessFlags(i)));
39 } 39 }
40 } 40 }
41 41
42 return c; 42 return c;
43 } 43 }
44 44
45 private static int protectify(int flags) { 45 private static int protectify(int flags) {
46 if (AccessFlag.isPrivate(flags)) { 46 if (AccessFlag.isPrivate(flags)) {
47 flags = AccessFlag.setProtected(flags); 47 flags = AccessFlag.setProtected(flags);
48 } 48 }
49 return flags; 49 return flags;
50 } 50 }
51} 51}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
index 0aa7fac..d627fe9 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import javassist.CtBehavior; 14import javassist.CtBehavior;
@@ -16,36 +17,35 @@ import javassist.CtField;
16import javassist.bytecode.AccessFlag; 17import javassist.bytecode.AccessFlag;
17import javassist.bytecode.InnerClassesAttribute; 18import javassist.bytecode.InnerClassesAttribute;
18 19
19
20public class ClassPublifier { 20public class ClassPublifier {
21 21
22 public static CtClass publify(CtClass c) { 22 public static CtClass publify(CtClass c) {
23 23
24 // publify all the fields 24 // publify all the fields
25 for (CtField field : c.getDeclaredFields()) { 25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(publify(field.getModifiers())); 26 field.setModifiers(publify(field.getModifiers()));
27 } 27 }
28 28
29 // publify all the methods and constructors 29 // publify all the methods and constructors
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 30 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
31 behavior.setModifiers(publify(behavior.getModifiers())); 31 behavior.setModifiers(publify(behavior.getModifiers()));
32 } 32 }
33 33
34 // publify all the inner classes 34 // publify all the inner classes
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
36 if (attr != null) { 36 if (attr != null) {
37 for (int i = 0; i < attr.tableLength(); i++) { 37 for (int i = 0; i < attr.tableLength(); i++) {
38 attr.setAccessFlags(i, publify(attr.accessFlags(i))); 38 attr.setAccessFlags(i, publify(attr.accessFlags(i)));
39 } 39 }
40 } 40 }
41 41
42 return c; 42 return c;
43 } 43 }
44 44
45 private static int publify(int flags) { 45 private static int publify(int flags) {
46 if (!AccessFlag.isPublic(flags)) { 46 if (!AccessFlag.isPublic(flags)) {
47 flags = AccessFlag.setPublic(flags); 47 flags = AccessFlag.setPublic(flags);
48 } 48 }
49 return flags; 49 return flags;
50 } 50 }
51} 51}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
index d874633..a52cab6 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -8,532 +8,532 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12 11
13import java.lang.reflect.InvocationTargetException; 12package cuchaz.enigma.bytecode;
14import java.lang.reflect.Method;
15import java.util.Arrays;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19 13
20import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
21import cuchaz.enigma.mapping.ClassNameReplacer; 15import cuchaz.enigma.mapping.ClassNameReplacer;
22import cuchaz.enigma.mapping.Mappings; 16import cuchaz.enigma.mapping.Mappings;
23import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
24import javassist.*; 18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
21import javassist.Modifier;
25import javassist.bytecode.*; 22import javassist.bytecode.*;
26import javassist.bytecode.SignatureAttribute.*; 23import javassist.bytecode.SignatureAttribute.*;
27 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
28public class ClassRenamer { 32public class ClassRenamer {
29 33
30 private enum SignatureType { 34 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) {
31 Class { 35 int mod = -1;
32 @Override 36 if (obj instanceof CtField)
33 public String rename(String signature, ReplacerClassMap map) { 37 mod = ((CtField) obj).getModifiers();
34 return renameClassSignature(signature, map); 38 else if (obj instanceof CtBehavior)
35 } 39 mod = ((CtBehavior) obj).getModifiers();
36 }, 40 else if (obj instanceof CtClass)
37 Field { 41 mod = ((CtClass) obj).getModifiers();
38 @Override 42
39 public String rename(String signature, ReplacerClassMap map) { 43 if (mod != -1) {
40 return renameFieldSignature(signature, map); 44 switch (modifier) {
41 } 45 case PRIVATE:
42 }, 46 mod = Modifier.setPrivate(mod);
43 Method { 47 break;
44 @Override 48 case PROTECTED:
45 public String rename(String signature, ReplacerClassMap map) { 49 mod = Modifier.setProtected(mod);
46 return renameMethodSignature(signature, map); 50 break;
47 } 51 case PUBLIC:
48 }; 52 mod = Modifier.setPublic(mod);
49 53 break;
50 public abstract String rename(String signature, ReplacerClassMap map); 54 default:
51 } 55 break;
52 56 }
53 private static class ReplacerClassMap extends HashMap<String, String> { 57 if (obj instanceof CtField)
54 58 ((CtField) obj).setModifiers(mod);
55 private ClassNameReplacer replacer; 59 else if (obj instanceof CtBehavior)
56 60 ((CtBehavior) obj).setModifiers(mod);
57 public ReplacerClassMap(ClassNameReplacer replacer) { 61 else
58 this.replacer = replacer; 62 ((CtClass) obj).setModifiers(mod);
59 } 63 }
60 64 }
61 @Override 65
62 public String get(Object obj) { 66 public static void renameClasses(CtClass c, final Translator translator) {
63 if (obj instanceof String) { 67 renameClasses(c, className -> {
64 return get((String) obj); 68 ClassEntry entry = translator.translateEntry(new ClassEntry(className));
65 } 69 if (entry != null) {
66 return null; 70 return entry.getName();
67 } 71 }
68 72 return null;
69 public String get(String className) { 73 });
70 return replacer.replace(className); 74 }
71 } 75
72 } 76 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) {
73 77 renameClasses(c, className -> {
74 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) 78 ClassEntry entry = new ClassEntry(className);
75 { 79 if (entry.isInDefaultPackage()) {
76 int mod = -1; 80 return newPackageName + "/" + entry.getName();
77 if (obj instanceof CtField) 81 }
78 mod = ((CtField) obj).getModifiers(); 82 return null;
79 else if (obj instanceof CtBehavior) 83 });
80 mod = ((CtBehavior) obj).getModifiers(); 84 }
81 else if (obj instanceof CtClass) 85
82 mod = ((CtClass) obj).getModifiers(); 86 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) {
83 87 renameClasses(c, className -> {
84 if (mod != -1) 88 ClassEntry entry = new ClassEntry(className);
85 { 89 if (entry.getPackageName().equals(oldPackageName)) {
86 switch (modifier) 90 return entry.getSimpleName();
87 { 91 }
88 case PRIVATE: 92 return null;
89 mod = Modifier.setPrivate(mod); 93 });
90 break; 94 }
91 case PROTECTED: 95
92 mod = Modifier.setProtected(mod); 96 @SuppressWarnings("unchecked")
93 break; 97 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
94 case PUBLIC: 98
95 mod = Modifier.setPublic(mod); 99 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
96 break; 100
97 default: 101 ReplacerClassMap map = new ReplacerClassMap(replacer);
98 break; 102 ClassFile classFile = c.getClassFile();
99 } 103
100 if (obj instanceof CtField) 104 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
101 ((CtField) obj).setModifiers(mod); 105 ConstPool constPool = c.getClassFile().getConstPool();
102 else if (obj instanceof CtBehavior) 106 constPool.renameClass(map);
103 ((CtBehavior) obj).setModifiers(mod); 107
104 else 108 // rename class attributes
105 ((CtClass) obj).setModifiers(mod); 109 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
106 } 110
107 } 111 // rename methods
108 112 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
109 public static void renameClasses(CtClass c, final Translator translator) { 113 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
110 renameClasses(c, className -> { 114 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
111 ClassEntry entry = translator.translateEntry(new ClassEntry(className)); 115 }
112 if (entry != null) { 116
113 return entry.getName(); 117 // rename fields
114 } 118 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
115 return null; 119 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
116 }); 120 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
117 } 121 }
118 122
119 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { 123 // rename the class name itself last
120 renameClasses(c, className -> { 124 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
121 ClassEntry entry = new ClassEntry(className); 125 // we only want to replace exactly this class name
122 if (entry.isInDefaultPackage()) { 126 String newName = renameClassName(c.getName(), map);
123 return newPackageName + "/" + entry.getName(); 127 if (newName != null) {
124 } 128 c.setName(newName);
125 return null; 129 }
126 }); 130
127 } 131 // replace simple names in the InnerClasses attribute too
128 132 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
129 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { 133 if (attr != null) {
130 renameClasses(c, className -> { 134 for (int i = 0; i < attr.tableLength(); i++) {
131 ClassEntry entry = new ClassEntry(className); 135
132 if (entry.getPackageName().equals(oldPackageName)) { 136 String innerName = attr.innerClass(i);
133 return entry.getSimpleName(); 137 // get the inner class full name (which has already been translated)
134 } 138 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
135 return null; 139
136 }); 140 if (attr.innerNameIndex(i) != 0) {
137 } 141 // update the inner name
138 142 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
139 @SuppressWarnings("unchecked") 143 }
140 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
141
142 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
143
144 ReplacerClassMap map = new ReplacerClassMap(replacer);
145 ClassFile classFile = c.getClassFile();
146
147 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
148 ConstPool constPool = c.getClassFile().getConstPool();
149 constPool.renameClass(map);
150
151 // rename class attributes
152 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
153
154 // rename methods
155 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
156 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
157 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
158 }
159
160 // rename fields
161 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
162 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
163 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
164 }
165
166 // rename the class name itself last
167 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
168 // we only want to replace exactly this class name
169 String newName = renameClassName(c.getName(), map);
170 if (newName != null) {
171 c.setName(newName);
172 }
173
174 // replace simple names in the InnerClasses attribute too
175 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
176 if (attr != null) {
177 for (int i = 0; i < attr.tableLength(); i++) {
178
179 String innerName = attr.innerClass(i);
180 // get the inner class full name (which has already been translated)
181 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
182
183 if (attr.innerNameIndex(i) != 0) {
184 // update the inner name
185 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
186 }
187 144
188 /* DEBUG 145 /* DEBUG
189 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); 146 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i)));
190 */ 147 */
191 } 148 }
192 } 149 }
193 } 150 }
194 151
195 @SuppressWarnings("unchecked") 152 @SuppressWarnings("unchecked")
196 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) { 153 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) {
197 try { 154 try {
198 155
199 // make the rename class method accessible 156 // make the rename class method accessible
200 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); 157 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class);
201 renameClassMethod.setAccessible(true); 158 renameClassMethod.setAccessible(true);
202 159
203 for (AttributeInfo attribute : attributes) { 160 for (AttributeInfo attribute : attributes) {
204 if (attribute instanceof SignatureAttribute) { 161 if (attribute instanceof SignatureAttribute) {
205 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell 162 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
206 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; 163 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute;
207 String newSignature = type.rename(signatureAttribute.getSignature(), map); 164 String newSignature = type.rename(signatureAttribute.getSignature(), map);
208 if (newSignature != null) { 165 if (newSignature != null) {
209 signatureAttribute.setSignature(newSignature); 166 signatureAttribute.setSignature(newSignature);
210 } 167 }
211 } else if (attribute instanceof CodeAttribute) { 168 } else if (attribute instanceof CodeAttribute) {
212 // code attributes have signature attributes too (indirectly) 169 // code attributes have signature attributes too (indirectly)
213 CodeAttribute codeAttribute = (CodeAttribute) attribute; 170 CodeAttribute codeAttribute = (CodeAttribute) attribute;
214 renameAttributes(codeAttribute.getAttributes(), map, type); 171 renameAttributes(codeAttribute.getAttributes(), map, type);
215 } else if (attribute instanceof LocalVariableTypeAttribute) { 172 } else if (attribute instanceof LocalVariableTypeAttribute) {
216 // lvt attributes have signature attributes too 173 // lvt attributes have signature attributes too
217 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; 174 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute;
218 renameLocalVariableTypeAttribute(localVariableAttribute, map); 175 renameLocalVariableTypeAttribute(localVariableAttribute, map);
219 } else { 176 } else {
220 renameClassMethod.invoke(attribute, map); 177 renameClassMethod.invoke(attribute, map);
221 } 178 }
222 } 179 }
223 180
224 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 181 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
225 throw new Error("Unable to call javassist methods by reflection!", ex); 182 throw new Error("Unable to call javassist methods by reflection!", ex);
226 } 183 }
227 } 184 }
228 185
229 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { 186 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
230 187
231 // adapted from LocalVariableAttribute.renameClass() 188 // adapted from LocalVariableAttribute.renameClass()
232 ConstPool cp = attribute.getConstPool(); 189 ConstPool cp = attribute.getConstPool();
233 int n = attribute.tableLength(); 190 int n = attribute.tableLength();
234 byte[] info = attribute.get(); 191 byte[] info = attribute.get();
235 for (int i = 0; i < n; ++i) { 192 for (int i = 0; i < n; ++i) {
236 int pos = i * 10 + 2; 193 int pos = i * 10 + 2;
237 int index = ByteArray.readU16bit(info, pos + 6); 194 int index = ByteArray.readU16bit(info, pos + 6);
238 if (index != 0) { 195 if (index != 0) {
239 String signature = cp.getUtf8Info(index); 196 String signature = cp.getUtf8Info(index);
240 String newSignature = renameLocalVariableSignature(signature, map); 197 String newSignature = renameLocalVariableSignature(signature, map);
241 if (newSignature != null) { 198 if (newSignature != null) {
242 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); 199 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6);
243 } 200 }
244 } 201 }
245 } 202 }
246 } 203 }
247 204
248 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { 205 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) {
249 206
250 // for some reason, signatures with . in them don't count as field signatures 207 // for some reason, signatures with . in them don't count as field signatures
251 // looks like anonymous classes delimit with . in stead of $ 208 // looks like anonymous classes delimit with . in stead of $
252 // convert the . to $, but keep track of how many we replace 209 // convert the . to $, but keep track of how many we replace
253 // we need to put them back after we translate 210 // we need to put them back after we translate
254 int start = signature.lastIndexOf('$') + 1; 211 int start = signature.lastIndexOf('$') + 1;
255 int numConverted = 0; 212 int numConverted = 0;
256 StringBuilder buf = new StringBuilder(signature); 213 StringBuilder buf = new StringBuilder(signature);
257 for (int i = buf.length() - 1; i >= start; i--) { 214 for (int i = buf.length() - 1; i >= start; i--) {
258 char c = buf.charAt(i); 215 char c = buf.charAt(i);
259 if (c == '.') { 216 if (c == '.') {
260 buf.setCharAt(i, '$'); 217 buf.setCharAt(i, '$');
261 numConverted++; 218 numConverted++;
262 } 219 }
263 } 220 }
264 signature = buf.toString(); 221 signature = buf.toString();
265 222
266 // translate 223 // translate
267 String newSignature = renameFieldSignature(signature, map); 224 String newSignature = renameFieldSignature(signature, map);
268 if (newSignature != null) { 225 if (newSignature != null) {
269 226
270 // put the delimiters back 227 // put the delimiters back
271 buf = new StringBuilder(newSignature); 228 buf = new StringBuilder(newSignature);
272 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { 229 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) {
273 char c = buf.charAt(i); 230 char c = buf.charAt(i);
274 if (c == '$') { 231 if (c == '$') {
275 buf.setCharAt(i, '.'); 232 buf.setCharAt(i, '.');
276 numConverted--; 233 numConverted--;
277 } 234 }
278 } 235 }
279 assert (numConverted == 0); 236 assert (numConverted == 0);
280 newSignature = buf.toString(); 237 newSignature = buf.toString();
281 238
282 return newSignature; 239 return newSignature;
283 } 240 }
284 241
285 return null; 242 return null;
286 } 243 }
287 244
288 private static String renameClassSignature(String signature, ReplacerClassMap map) { 245 private static String renameClassSignature(String signature, ReplacerClassMap map) {
289 try { 246 try {
290 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); 247 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map);
291 return type.encode(); 248 return type.encode();
292 } catch (BadBytecode ex) { 249 } catch (BadBytecode ex) {
293 throw new Error("Can't parse field signature: " + signature); 250 throw new Error("Can't parse field signature: " + signature);
294 } 251 }
295 } 252 }
296 253
297 private static String renameFieldSignature(String signature, ReplacerClassMap map) { 254 private static String renameFieldSignature(String signature, ReplacerClassMap map) {
298 try { 255 try {
299 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); 256 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map);
300 if (type != null) { 257 if (type != null) {
301 return type.encode(); 258 return type.encode();
302 } 259 }
303 return null; 260 return null;
304 } catch (BadBytecode ex) { 261 } catch (BadBytecode ex) {
305 throw new Error("Can't parse class signature: " + signature); 262 throw new Error("Can't parse class signature: " + signature);
306 } 263 }
307 } 264 }
308 265
309 private static String renameMethodSignature(String signature, ReplacerClassMap map) { 266 private static String renameMethodSignature(String signature, ReplacerClassMap map) {
310 try { 267 try {
311 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); 268 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map);
312 return type.encode(); 269 return type.encode();
313 } catch (BadBytecode ex) { 270 } catch (BadBytecode ex) {
314 throw new Error("Can't parse method signature: " + signature); 271 throw new Error("Can't parse method signature: " + signature);
315 } 272 }
316 } 273 }
317 274
318 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) 275 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) {
319 { 276 if (typeParamTypes != null) {
320 if (typeParamTypes != null) { 277 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length);
321 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); 278 for (int i = 0; i < typeParamTypes.length; i++) {
322 for (int i = 0; i < typeParamTypes.length; i++) { 279 TypeParameter newParamType = renameType(typeParamTypes[i], map);
323 TypeParameter newParamType = renameType(typeParamTypes[i], map); 280 if (newParamType != null) {
324 if (newParamType != null) { 281 typeParamTypes[i] = newParamType;
325 typeParamTypes[i] = newParamType; 282 }
326 } 283 }
327 } 284 }
328 } 285 return typeParamTypes;
329 return typeParamTypes; 286 }
330 } 287
331 288 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) {
332 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { 289
333 290 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map);
334 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); 291
335 292 ClassType superclassType = type.getSuperClass();
336 ClassType superclassType = type.getSuperClass(); 293 if (superclassType != ClassType.OBJECT) {
337 if (superclassType != ClassType.OBJECT) { 294 ClassType newSuperclassType = renameType(superclassType, map);
338 ClassType newSuperclassType = renameType(superclassType, map); 295 if (newSuperclassType != null) {
339 if (newSuperclassType != null) { 296 superclassType = newSuperclassType;
340 superclassType = newSuperclassType; 297 }
341 } 298 }
342 } 299
343 300 ClassType[] interfaceTypes = type.getInterfaces();
344 ClassType[] interfaceTypes = type.getInterfaces(); 301 if (interfaceTypes != null) {
345 if (interfaceTypes != null) { 302 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
346 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); 303 for (int i = 0; i < interfaceTypes.length; i++) {
347 for (int i = 0; i < interfaceTypes.length; i++) { 304 ClassType newInterfaceType = renameType(interfaceTypes[i], map);
348 ClassType newInterfaceType = renameType(interfaceTypes[i], map); 305 if (newInterfaceType != null) {
349 if (newInterfaceType != null) { 306 interfaceTypes[i] = newInterfaceType;
350 interfaceTypes[i] = newInterfaceType; 307 }
351 } 308 }
352 } 309 }
353 } 310
354 311 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes);
355 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); 312 }
356 } 313
357 314 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) {
358 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { 315
359 316 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map);
360 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); 317
361 318 Type[] paramTypes = type.getParameterTypes();
362 Type[] paramTypes = type.getParameterTypes(); 319 if (paramTypes != null) {
363 if (paramTypes != null) { 320 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
364 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); 321 for (int i = 0; i < paramTypes.length; i++) {
365 for (int i = 0; i < paramTypes.length; i++) { 322 Type newParamType = renameType(paramTypes[i], map);
366 Type newParamType = renameType(paramTypes[i], map); 323 if (newParamType != null) {
367 if (newParamType != null) { 324 paramTypes[i] = newParamType;
368 paramTypes[i] = newParamType; 325 }
369 } 326 }
370 } 327 }
371 } 328
372 329 Type returnType = type.getReturnType();
373 Type returnType = type.getReturnType(); 330 if (returnType != null) {
374 if (returnType != null) { 331 Type newReturnType = renameType(returnType, map);
375 Type newReturnType = renameType(returnType, map); 332 if (newReturnType != null) {
376 if (newReturnType != null) { 333 returnType = newReturnType;
377 returnType = newReturnType; 334 }
378 } 335 }
379 } 336
380 337 ObjectType[] exceptionTypes = type.getExceptionTypes();
381 ObjectType[] exceptionTypes = type.getExceptionTypes(); 338 if (exceptionTypes != null) {
382 if (exceptionTypes != null) { 339 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length);
383 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); 340 for (int i = 0; i < exceptionTypes.length; i++) {
384 for (int i = 0; i < exceptionTypes.length; i++) { 341 ObjectType newExceptionType = renameType(exceptionTypes[i], map);
385 ObjectType newExceptionType = renameType(exceptionTypes[i], map); 342 if (newExceptionType != null) {
386 if (newExceptionType != null) { 343 exceptionTypes[i] = newExceptionType;
387 exceptionTypes[i] = newExceptionType; 344 }
388 } 345 }
389 } 346 }
390 } 347
391 348 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes);
392 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); 349 }
393 } 350
394 351 private static Type renameType(Type type, ReplacerClassMap map) {
395 private static Type renameType(Type type, ReplacerClassMap map) { 352 if (type instanceof ObjectType) {
396 if (type instanceof ObjectType) { 353 return renameType((ObjectType) type, map);
397 return renameType((ObjectType) type, map); 354 } else if (type instanceof BaseType) {
398 } else if (type instanceof BaseType) { 355 return renameType((BaseType) type, map);
399 return renameType((BaseType) type, map); 356 } else {
400 } else { 357 throw new Error("Don't know how to rename type " + type.getClass());
401 throw new Error("Don't know how to rename type " + type.getClass()); 358 }
402 } 359 }
403 } 360
404 361 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) {
405 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { 362 if (type instanceof ArrayType) {
406 if (type instanceof ArrayType) { 363 return renameType((ArrayType) type, map);
407 return renameType((ArrayType) type, map); 364 } else if (type instanceof ClassType) {
408 } else if (type instanceof ClassType) { 365 return renameType((ClassType) type, map);
409 return renameType((ClassType) type, map); 366 } else if (type instanceof TypeVariable) {
410 } else if (type instanceof TypeVariable) { 367 return renameType((TypeVariable) type, map);
411 return renameType((TypeVariable) type, map); 368 } else {
412 } else { 369 throw new Error("Don't know how to rename type " + type.getClass());
413 throw new Error("Don't know how to rename type " + type.getClass()); 370 }
414 } 371 }
415 } 372
416 373 private static BaseType renameType(BaseType type, ReplacerClassMap map) {
417 private static BaseType renameType(BaseType type, ReplacerClassMap map) { 374 // don't have to rename primitives
418 // don't have to rename primitives 375 return null;
419 return null; 376 }
420 } 377
421 378 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) {
422 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { 379 // don't have to rename template args
423 // don't have to rename template args 380 return null;
424 return null; 381 }
425 } 382
426 383 private static ClassType renameType(ClassType type, ReplacerClassMap map) {
427 private static ClassType renameType(ClassType type, ReplacerClassMap map) { 384
428 385 // translate type args
429 // translate type args 386 TypeArgument[] args = type.getTypeArguments();
430 TypeArgument[] args = type.getTypeArguments(); 387 if (args != null) {
431 if (args != null) { 388 args = Arrays.copyOf(args, args.length);
432 args = Arrays.copyOf(args, args.length); 389 for (int i = 0; i < args.length; i++) {
433 for (int i = 0; i < args.length; i++) { 390 TypeArgument newType = renameType(args[i], map);
434 TypeArgument newType = renameType(args[i], map); 391 if (newType != null) {
435 if (newType != null) { 392 args[i] = newType;
436 args[i] = newType; 393 }
437 } 394 }
438 } 395 }
439 } 396
440 397 if (type instanceof NestedClassType) {
441 if (type instanceof NestedClassType) { 398 NestedClassType nestedType = (NestedClassType) type;
442 NestedClassType nestedType = (NestedClassType) type; 399
443 400 // translate the name
444 // translate the name 401 String name = getClassName(type);
445 String name = getClassName(type); 402 String newName = map.get(name);
446 String newName = map.get(name); 403 if (newName != null) {
447 if (newName != null) { 404 name = new ClassEntry(newName).getInnermostClassName();
448 name = new ClassEntry(newName).getInnermostClassName(); 405 }
449 } 406
450 407 // translate the parent class too
451 // translate the parent class too 408 ClassType parent = renameType(nestedType.getDeclaringClass(), map);
452 ClassType parent = renameType(nestedType.getDeclaringClass(), map); 409 if (parent == null) {
453 if (parent == null) { 410 parent = nestedType.getDeclaringClass();
454 parent = nestedType.getDeclaringClass(); 411 }
455 } 412
456 413 return new NestedClassType(parent, name, args);
457 return new NestedClassType(parent, name, args); 414 } else {
458 } else { 415
459 416 // translate the name
460 // translate the name 417 String name = type.getName();
461 String name = type.getName(); 418 String newName = renameClassName(name, map);
462 String newName = renameClassName(name, map); 419 if (newName != null) {
463 if (newName != null) { 420 name = newName;
464 name = newName; 421 }
465 } 422
466 423 return new ClassType(name, args);
467 return new ClassType(name, args); 424 }
468 } 425 }
469 } 426
470 427 private static String getClassName(ClassType type) {
471 private static String getClassName(ClassType type) { 428 if (type instanceof NestedClassType) {
472 if (type instanceof NestedClassType) { 429 NestedClassType nestedType = (NestedClassType) type;
473 NestedClassType nestedType = (NestedClassType) type; 430 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$'));
474 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); 431 } else {
475 } else { 432 return Descriptor.toJvmName(type.getName());
476 return Descriptor.toJvmName(type.getName()); 433 }
477 } 434 }
478 } 435
479 436 private static String renameClassName(String name, ReplacerClassMap map) {
480 private static String renameClassName(String name, ReplacerClassMap map) { 437 String newName = map.get(Descriptor.toJvmName(name));
481 String newName = map.get(Descriptor.toJvmName(name)); 438 if (newName != null) {
482 if (newName != null) { 439 return Descriptor.toJavaName(newName);
483 return Descriptor.toJavaName(newName); 440 }
484 } 441 return null;
485 return null; 442 }
486 } 443
487 444 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) {
488 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { 445 ObjectType subType = type.getType();
489 ObjectType subType = type.getType(); 446 if (subType != null) {
490 if (subType != null) { 447 ObjectType newSubType = renameType(subType, map);
491 ObjectType newSubType = renameType(subType, map); 448 if (newSubType != null) {
492 if (newSubType != null) { 449 switch (type.getKind()) {
493 switch (type.getKind()) { 450 case ' ':
494 case ' ': 451 return new TypeArgument(newSubType);
495 return new TypeArgument(newSubType); 452 case '+':
496 case '+': 453 return TypeArgument.subclassOf(newSubType);
497 return TypeArgument.subclassOf(newSubType); 454 case '-':
498 case '-': 455 return TypeArgument.superOf(newSubType);
499 return TypeArgument.superOf(newSubType); 456 default:
500 default: 457 throw new Error("Unknown type kind: " + type.getKind());
501 throw new Error("Unknown type kind: " + type.getKind()); 458 }
502 } 459 }
503 } 460 }
504 } 461 return null;
505 return null; 462 }
506 } 463
507 464 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) {
508 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { 465 Type newSubType = renameType(type.getComponentType(), map);
509 Type newSubType = renameType(type.getComponentType(), map); 466 if (newSubType != null) {
510 if (newSubType != null) { 467 return new ArrayType(type.getDimension(), newSubType);
511 return new ArrayType(type.getDimension(), newSubType); 468 }
512 } 469 return null;
513 return null; 470 }
514 } 471
515 472 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) {
516 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { 473
517 474 ObjectType superclassType = type.getClassBound();
518 ObjectType superclassType = type.getClassBound(); 475 if (superclassType != null) {
519 if (superclassType != null) { 476 ObjectType newSuperclassType = renameType(superclassType, map);
520 ObjectType newSuperclassType = renameType(superclassType, map); 477 if (newSuperclassType != null) {
521 if (newSuperclassType != null) { 478 superclassType = newSuperclassType;
522 superclassType = newSuperclassType; 479 }
523 } 480 }
524 } 481
525 482 ObjectType[] interfaceTypes = type.getInterfaceBound();
526 ObjectType[] interfaceTypes = type.getInterfaceBound(); 483 if (interfaceTypes != null) {
527 if (interfaceTypes != null) { 484 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
528 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); 485 for (int i = 0; i < interfaceTypes.length; i++) {
529 for (int i = 0; i < interfaceTypes.length; i++) { 486 ObjectType newInterfaceType = renameType(interfaceTypes[i], map);
530 ObjectType newInterfaceType = renameType(interfaceTypes[i], map); 487 if (newInterfaceType != null) {
531 if (newInterfaceType != null) { 488 interfaceTypes[i] = newInterfaceType;
532 interfaceTypes[i] = newInterfaceType; 489 }
533 } 490 }
534 } 491 }
535 } 492
536 493 return new TypeParameter(type.getName(), superclassType, interfaceTypes);
537 return new TypeParameter(type.getName(), superclassType, interfaceTypes); 494 }
538 } 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} 539}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
index 62ebfaf..1ebf656 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -8,155 +8,158 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
14import cuchaz.enigma.mapping.Translator; 15import javassist.CtBehavior;
15import javassist.*; 16import javassist.CtClass;
17import javassist.CtField;
18import javassist.CtMethod;
16import javassist.bytecode.*; 19import javassist.bytecode.*;
17 20
18public class ClassTranslator { 21public class ClassTranslator {
19 22
20 private Translator translator; 23 private Translator translator;
21 24
22 public ClassTranslator(Translator translator) { 25 public ClassTranslator(Translator translator) {
23 this.translator = translator; 26 this.translator = translator;
24 } 27 }
25 28
26 public void translate(CtClass c) { 29 public void translate(CtClass c) {
27 30
28 // NOTE: the order of these translations is very important 31 // NOTE: the order of these translations is very important
29 32
30 // translate all the field and method references in the code by editing the constant pool 33 // translate all the field and method references in the code by editing the constant pool
31 ConstPool constants = c.getClassFile().getConstPool(); 34 ConstPool constants = c.getClassFile().getConstPool();
32 ConstPoolEditor editor = new ConstPoolEditor(constants); 35 ConstPoolEditor editor = new ConstPoolEditor(constants);
33 for (int i = 1; i < constants.getSize(); i++) { 36 for (int i = 1; i < constants.getSize(); i++) {
34 switch (constants.getTag(i)) { 37 switch (constants.getTag(i)) {
35 38
36 case ConstPool.CONST_Fieldref: { 39 case ConstPool.CONST_Fieldref: {
37 40
38 // translate the name and type 41 // translate the name and type
39 FieldEntry entry = EntryFactory.getFieldEntry( 42 FieldEntry entry = EntryFactory.getFieldEntry(
40 Descriptor.toJvmName(constants.getFieldrefClassName(i)), 43 Descriptor.toJvmName(constants.getFieldrefClassName(i)),
41 constants.getFieldrefName(i), 44 constants.getFieldrefName(i),
42 constants.getFieldrefType(i) 45 constants.getFieldrefType(i)
43 ); 46 );
44 FieldEntry translatedEntry = this.translator.translateEntry(entry); 47 FieldEntry translatedEntry = this.translator.translateEntry(entry);
45 if (!entry.equals(translatedEntry)) { 48 if (!entry.equals(translatedEntry)) {
46 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); 49 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString());
47 } 50 }
48 } 51 }
49 break; 52 break;
50 53
51 case ConstPool.CONST_Methodref: 54 case ConstPool.CONST_Methodref:
52 case ConstPool.CONST_InterfaceMethodref: { 55 case ConstPool.CONST_InterfaceMethodref: {
53 56
54 // translate the name and type (ie signature) 57 // translate the name and type (ie signature)
55 BehaviorEntry entry = EntryFactory.getBehaviorEntry( 58 BehaviorEntry entry = EntryFactory.getBehaviorEntry(
56 Descriptor.toJvmName(editor.getMemberrefClassname(i)), 59 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
57 editor.getMemberrefName(i), 60 editor.getMemberrefName(i),
58 editor.getMemberrefType(i) 61 editor.getMemberrefType(i)
59 ); 62 );
60 BehaviorEntry translatedEntry = this.translator.translateEntry(entry); 63 BehaviorEntry translatedEntry = this.translator.translateEntry(entry);
61 if (!entry.equals(translatedEntry)) { 64 if (!entry.equals(translatedEntry)) {
62 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); 65 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString());
63 } 66 }
64 } 67 }
65 break; 68 break;
66 default: 69 default:
67 break; 70 break;
68 } 71 }
69 } 72 }
70 73
71 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 74 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
72 Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); 75 Mappings.EntryModifier modifier = this.translator.getModifier(classEntry);
73 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 76 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
74 ClassRenamer.applyModifier(c, modifier); 77 ClassRenamer.applyModifier(c, modifier);
75 78
76 // translate all the fields 79 // translate all the fields
77 for (CtField field : c.getDeclaredFields()) { 80 for (CtField field : c.getDeclaredFields()) {
78 81
79 // translate the name 82 // translate the name
80 FieldEntry entry = EntryFactory.getFieldEntry(field); 83 FieldEntry entry = EntryFactory.getFieldEntry(field);
81 String translatedName = this.translator.translate(entry); 84 String translatedName = this.translator.translate(entry);
82 modifier = this.translator.getModifier(entry); 85 modifier = this.translator.getModifier(entry);
83 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 86 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
84 ClassRenamer.applyModifier(field, modifier); 87 ClassRenamer.applyModifier(field, modifier);
85 88
86 if (translatedName != null) { 89 if (translatedName != null) {
87 field.setName(translatedName); 90 field.setName(translatedName);
88 } 91 }
89 92
90 // translate the type 93 // translate the type
91 Type translatedType = this.translator.translateType(entry.getType()); 94 Type translatedType = this.translator.translateType(entry.getType());
92 field.getFieldInfo().setDescriptor(translatedType.toString()); 95 field.getFieldInfo().setDescriptor(translatedType.toString());
93 } 96 }
94 97
95 // translate all the methods and constructors 98 // translate all the methods and constructors
96 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 99 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
97 100
98 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); 101 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
99 102
100 modifier = this.translator.getModifier(entry); 103 modifier = this.translator.getModifier(entry);
101 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 104 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
102 ClassRenamer.applyModifier(behavior, modifier); 105 ClassRenamer.applyModifier(behavior, modifier);
103 106
104 if (behavior instanceof CtMethod) { 107 if (behavior instanceof CtMethod) {
105 CtMethod method = (CtMethod) behavior; 108 CtMethod method = (CtMethod) behavior;
106 109
107 // translate the name 110 // translate the name
108 String translatedName = this.translator.translate(entry); 111 String translatedName = this.translator.translate(entry);
109 if (translatedName != null) { 112 if (translatedName != null) {
110 method.setName(translatedName); 113 method.setName(translatedName);
111 } 114 }
112 } 115 }
113 116
114 if (entry.getSignature() != null) { 117 if (entry.getSignature() != null) {
115 // translate the signature 118 // translate the signature
116 Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); 119 Signature translatedSignature = this.translator.translateSignature(entry.getSignature());
117 behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); 120 behavior.getMethodInfo().setDescriptor(translatedSignature.toString());
118 } 121 }
119 } 122 }
120 123
121 // translate the EnclosingMethod attribute 124 // translate the EnclosingMethod attribute
122 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 125 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
123 if (enclosingMethodAttr != null) { 126 if (enclosingMethodAttr != null) {
124 127
125 if (enclosingMethodAttr.methodIndex() == 0) { 128 if (enclosingMethodAttr.methodIndex() == 0) {
126 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); 129 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className()));
127 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); 130 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry);
128 c.getClassFile().addAttribute(new EnclosingMethodAttribute( 131 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
129 constants, 132 constants,
130 deobfBehaviorEntry.getClassName() 133 deobfBehaviorEntry.getClassName()
131 )); 134 ));
132 } else { 135 } else {
133 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( 136 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(
134 Descriptor.toJvmName(enclosingMethodAttr.className()), 137 Descriptor.toJvmName(enclosingMethodAttr.className()),
135 enclosingMethodAttr.methodName(), 138 enclosingMethodAttr.methodName(),
136 enclosingMethodAttr.methodDescriptor() 139 enclosingMethodAttr.methodDescriptor()
137 ); 140 );
138 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); 141 BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry);
139 c.getClassFile().addAttribute(new EnclosingMethodAttribute( 142 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
140 constants, 143 constants,
141 deobfBehaviorEntry.getClassName(), 144 deobfBehaviorEntry.getClassName(),
142 deobfBehaviorEntry.getName(), 145 deobfBehaviorEntry.getName(),
143 deobfBehaviorEntry.getSignature().toString() 146 deobfBehaviorEntry.getSignature().toString()
144 )); 147 ));
145 } 148 }
146 } 149 }
147 150
148 // translate all the class names referenced in the code 151 // translate all the class names referenced in the code
149 // the above code only changed method/field/reference names and types, but not the rest of the class references 152 // the above code only changed method/field/reference names and types, but not the rest of the class references
150 ClassRenamer.renameClasses(c, this.translator); 153 ClassRenamer.renameClasses(c, this.translator);
151 154
152 // translate the source file attribute too 155 // translate the source file attribute too
153 ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); 156 ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry);
154 if (deobfClassEntry != null) { 157 if (deobfClassEntry != null) {
155 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; 158 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
156 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); 159 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
157 } 160 }
158 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 161 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
159 if (attr != null) 162 if (attr != null)
160 InnerClassWriter.changeModifier(c, attr, translator); 163 InnerClassWriter.changeModifier(c, attr, translator);
161 } 164 }
162} 165}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
index 256df61..1932730 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
@@ -8,8 +8,15 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 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
13import java.io.DataInputStream; 20import java.io.DataInputStream;
14import java.io.DataOutputStream; 21import java.io.DataOutputStream;
15import java.lang.reflect.Constructor; 22import java.lang.reflect.Constructor;
@@ -17,247 +24,241 @@ import java.lang.reflect.Field;
17import java.lang.reflect.Method; 24import java.lang.reflect.Method;
18import java.util.HashMap; 25import java.util.HashMap;
19 26
20import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
21import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
22import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
23import javassist.bytecode.ConstPool;
24import javassist.bytecode.Descriptor;
25
26public class ConstPoolEditor { 27public class ConstPoolEditor {
27 28
28 private static Method getItem; 29 private static Method getItem;
29 private static Method addItem; 30 private static Method addItem;
30 private static Method addItem0; 31 private static Method addItem0;
31 private static Field items; 32 private static Field items;
32 private static Field cache; 33 private static Field cache;
33 private static Field numItems; 34 private static Field numItems;
34 private static Field objects; 35 private static Field objects;
35 private static Field elements; 36 private static Field elements;
36 private static Method methodWritePool; 37 private static Method methodWritePool;
37 private static Constructor<ConstPool> constructorPool; 38 private static Constructor<ConstPool> constructorPool;
38 39
39 static { 40 static {
40 try { 41 try {
41 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); 42 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class);
42 getItem.setAccessible(true); 43 getItem.setAccessible(true);
43 44
44 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); 45 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo"));
45 addItem.setAccessible(true); 46 addItem.setAccessible(true);
46 47
47 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); 48 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo"));
48 addItem0.setAccessible(true); 49 addItem0.setAccessible(true);
49 50
50 items = ConstPool.class.getDeclaredField("items"); 51 items = ConstPool.class.getDeclaredField("items");
51 items.setAccessible(true); 52 items.setAccessible(true);
52 53
53 cache = ConstPool.class.getDeclaredField("itemsCache"); 54 cache = ConstPool.class.getDeclaredField("itemsCache");
54 cache.setAccessible(true); 55 cache.setAccessible(true);
55 56
56 numItems = ConstPool.class.getDeclaredField("numOfItems"); 57 numItems = ConstPool.class.getDeclaredField("numOfItems");
57 numItems.setAccessible(true); 58 numItems.setAccessible(true);
58 59
59 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); 60 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects");
60 objects.setAccessible(true); 61 objects.setAccessible(true);
61 62
62 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); 63 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements");
63 elements.setAccessible(true); 64 elements.setAccessible(true);
64 65
65 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); 66 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class);
66 methodWritePool.setAccessible(true); 67 methodWritePool.setAccessible(true);
67 68
68 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); 69 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class);
69 constructorPool.setAccessible(true); 70 constructorPool.setAccessible(true);
70 } catch (Exception ex) { 71 } catch (Exception ex) {
71 throw new Error(ex); 72 throw new Error(ex);
72 } 73 }
73 } 74 }
74 75
75 private ConstPool pool; 76 private ConstPool pool;
76 77
77 public ConstPoolEditor(ConstPool pool) { 78 public ConstPoolEditor(ConstPool pool) {
78 this.pool = pool; 79 this.pool = pool;
79 } 80 }
80 81
81 public void writePool(DataOutputStream out) { 82 public static ConstPool readPool(DataInputStream in) {
82 try { 83 try {
83 methodWritePool.invoke(this.pool, out); 84 return constructorPool.newInstance(in);
84 } catch (Exception ex) { 85 } catch (Exception ex) {
85 throw new Error(ex); 86 throw new Error(ex);
86 } 87 }
87 } 88 }
88 89
89 public static ConstPool readPool(DataInputStream in) { 90 public static ConstPool newConstPool() {
90 try { 91 // const pool expects the name of a class to initialize itself
91 return constructorPool.newInstance(in); 92 // but we want an empty pool
92 } catch (Exception ex) { 93 // so give it a bogus name, and then clear the entries afterwards
93 throw new Error(ex); 94 ConstPool pool = new ConstPool("a");
94 } 95
95 } 96 ConstPoolEditor editor = new ConstPoolEditor(pool);
96 97 int size = pool.getSize();
97 public String getMemberrefClassname(int memberrefIndex) { 98 for (int i = 0; i < size - 1; i++) {
98 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); 99 editor.removeLastItem();
99 } 100 }
100 101
101 public String getMemberrefName(int memberrefIndex) { 102 // make sure the pool is actually empty
102 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); 103 // although, in this case "empty" means one thing in it
103 } 104 // the JVM spec says index 0 should be reserved
104 105 assert (pool.getSize() == 1);
105 public String getMemberrefType(int memberrefIndex) { 106 assert (editor.getItem(0) == null);
106 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); 107 assert (editor.getItem(1) == null);
107 } 108 assert (editor.getItem(2) == null);
108 109 assert (editor.getItem(3) == null);
109 public ConstInfoAccessor getItem(int index) { 110
110 try { 111 // also, clear the cache
111 Object entry = getItem.invoke(this.pool, index); 112 editor.getCache().clear();
112 if (entry == null) { 113
113 return null; 114 return pool;
114 } 115 }
115 return new ConstInfoAccessor(entry); 116
116 } catch (Exception ex) { 117 public void writePool(DataOutputStream out) {
117 throw new Error(ex); 118 try {
118 } 119 methodWritePool.invoke(this.pool, out);
119 } 120 } catch (Exception ex) {
120 121 throw new Error(ex);
121 public int addItem(Object item) { 122 }
122 try { 123 }
123 return (Integer) addItem.invoke(this.pool, item); 124
124 } catch (Exception ex) { 125 public String getMemberrefClassname(int memberrefIndex) {
125 throw new Error(ex); 126 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex)));
126 } 127 }
127 } 128
128 129 public String getMemberrefName(int memberrefIndex) {
129 public int addItemForceNew(Object item) { 130 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex)));
130 try { 131 }
131 return (Integer) addItem0.invoke(this.pool, item); 132
132 } catch (Exception ex) { 133 public String getMemberrefType(int memberrefIndex) {
133 throw new Error(ex); 134 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex)));
134 } 135 }
135 } 136
136 137 public ConstInfoAccessor getItem(int index) {
137 @SuppressWarnings("rawtypes") 138 try {
138 public void removeLastItem() { 139 Object entry = getItem.invoke(this.pool, index);
139 try { 140 if (entry == null) {
140 // remove the item from the cache 141 return null;
141 HashMap cache = getCache(); 142 }
142 if (cache != null) { 143 return new ConstInfoAccessor(entry);
143 Object item = getItem(this.pool.getSize() - 1); 144 } catch (Exception ex) {
144 cache.remove(item); 145 throw new Error(ex);
145 } 146 }
146 147 }
147 // remove the actual item 148
148 // based off of LongVector.addElement() 149 public int addItem(Object item) {
149 Object item = items.get(this.pool); 150 try {
150 Object[][] object = (Object[][]) objects.get(items); 151 return (Integer) addItem.invoke(this.pool, item);
151 int numElements = (Integer) elements.get(items) - 1; 152 } catch (Exception ex) {
152 int nth = numElements >> 7; 153 throw new Error(ex);
153 int offset = numElements & (128 - 1); 154 }
154 object[nth][offset] = null; 155 }
155 156
156 // decrement the number of items 157 public int addItemForceNew(Object item) {
157 elements.set(item, numElements); 158 try {
158 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); 159 return (Integer) addItem0.invoke(this.pool, item);
159 } catch (Exception ex) { 160 } catch (Exception ex) {
160 throw new Error(ex); 161 throw new Error(ex);
161 } 162 }
162 } 163 }
163 164
164 @SuppressWarnings("rawtypes") 165 @SuppressWarnings("rawtypes")
165 public HashMap getCache() { 166 public void removeLastItem() {
166 try { 167 try {
167 return (HashMap) cache.get(this.pool); 168 // remove the item from the cache
168 } catch (Exception ex) { 169 HashMap cache = getCache();
169 throw new Error(ex); 170 if (cache != null) {
170 } 171 Object item = getItem(this.pool.getSize() - 1);
171 } 172 cache.remove(item);
172 173 }
173 @SuppressWarnings({"rawtypes", "unchecked"}) 174
174 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { 175 // remove the actual item
175 // NOTE: when changing values, we always need to copy-on-write 176 // based off of LongVector.addElement()
176 try { 177 Object item = items.get(this.pool);
177 // get the memberref item 178 Object[][] object = (Object[][]) objects.get(items);
178 Object item = getItem(memberrefIndex).getItem(); 179 int numElements = (Integer) elements.get(items) - 1;
179 180 int nth = numElements >> 7;
180 // update the cache 181 int offset = numElements & (128 - 1);
181 HashMap cache = getCache(); 182 object[nth][offset] = null;
182 if (cache != null) { 183
183 cache.remove(item); 184 // decrement the number of items
184 } 185 elements.set(item, numElements);
185 186 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1);
186 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); 187 } catch (Exception ex) {
187 188 throw new Error(ex);
188 // update the cache 189 }
189 if (cache != null) { 190 }
190 cache.put(item, item); 191
191 } 192 @SuppressWarnings("rawtypes")
192 } catch (Exception ex) { 193 public HashMap getCache() {
193 throw new Error(ex); 194 try {
194 } 195 return (HashMap) cache.get(this.pool);
195 196 } catch (Exception ex) {
196 // make sure the change worked 197 throw new Error(ex);
197 assert (newName.equals(getMemberrefName(memberrefIndex))); 198 }
198 assert (newType.equals(getMemberrefType(memberrefIndex))); 199 }
199 } 200
200 201 @SuppressWarnings({ "rawtypes", "unchecked" })
201 @SuppressWarnings({"rawtypes", "unchecked"}) 202 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) {
202 public void changeClassName(int classNameIndex, String newName) { 203 // NOTE: when changing values, we always need to copy-on-write
203 // NOTE: when changing values, we always need to copy-on-write 204 try {
204 try { 205 // get the memberref item
205 // get the class item 206 Object item = getItem(memberrefIndex).getItem();
206 Object item = getItem(classNameIndex).getItem(); 207
207 208 // update the cache
208 // update the cache 209 HashMap cache = getCache();
209 HashMap cache = getCache(); 210 if (cache != null) {
210 if (cache != null) { 211 cache.remove(item);
211 cache.remove(item); 212 }
212 } 213
213 214 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType));
214 // add the new name and repoint the name-and-type to it 215
215 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); 216 // update the cache
216 217 if (cache != null) {
217 // update the cache 218 cache.put(item, item);
218 if (cache != null) { 219 }
219 cache.put(item, item); 220 } catch (Exception ex) {
220 } 221 throw new Error(ex);
221 } catch (Exception ex) { 222 }
222 throw new Error(ex); 223
223 } 224 // make sure the change worked
224 } 225 assert (newName.equals(getMemberrefName(memberrefIndex)));
225 226 assert (newType.equals(getMemberrefType(memberrefIndex)));
226 public static ConstPool newConstPool() { 227 }
227 // const pool expects the name of a class to initialize itself 228
228 // but we want an empty pool 229 @SuppressWarnings({ "rawtypes", "unchecked" })
229 // so give it a bogus name, and then clear the entries afterwards 230 public void changeClassName(int classNameIndex, String newName) {
230 ConstPool pool = new ConstPool("a"); 231 // NOTE: when changing values, we always need to copy-on-write
231 232 try {
232 ConstPoolEditor editor = new ConstPoolEditor(pool); 233 // get the class item
233 int size = pool.getSize(); 234 Object item = getItem(classNameIndex).getItem();
234 for (int i = 0; i < size - 1; i++) { 235
235 editor.removeLastItem(); 236 // update the cache
236 } 237 HashMap cache = getCache();
237 238 if (cache != null) {
238 // make sure the pool is actually empty 239 cache.remove(item);
239 // although, in this case "empty" means one thing in it 240 }
240 // the JVM spec says index 0 should be reserved 241
241 assert (pool.getSize() == 1); 242 // add the new name and repoint the name-and-type to it
242 assert (editor.getItem(0) == null); 243 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName));
243 assert (editor.getItem(1) == null); 244
244 assert (editor.getItem(2) == null); 245 // update the cache
245 assert (editor.getItem(3) == null); 246 if (cache != null) {
246 247 cache.put(item, item);
247 // also, clear the cache 248 }
248 editor.getCache().clear(); 249 } catch (Exception ex) {
249 250 throw new Error(ex);
250 return pool; 251 }
251 } 252 }
252 253
253 public String dump() { 254 public String dump() {
254 StringBuilder buf = new StringBuilder(); 255 StringBuilder buf = new StringBuilder();
255 for (int i = 1; i < this.pool.getSize(); i++) { 256 for (int i = 1; i < this.pool.getSize(); i++) {
256 buf.append(String.format("%4d", i)); 257 buf.append(String.format("%4d", i));
257 buf.append(" "); 258 buf.append(" ");
258 buf.append(getItem(i).toString()); 259 buf.append(getItem(i));
259 buf.append("\n"); 260 buf.append("\n");
260 } 261 }
261 return buf.toString(); 262 return buf.toString();
262 } 263 }
263} 264}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
index 21b0417..9013d58 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
@@ -8,259 +8,259 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import cuchaz.enigma.bytecode.accessors.*;
14 16
15import java.util.Collection; 17import java.util.Collection;
16import java.util.Map; 18import java.util.Map;
17 19
18import cuchaz.enigma.bytecode.accessors.*;
19
20public enum InfoType { 20public enum InfoType {
21 21
22 Utf8Info(1), 22 Utf8Info(1),
23 IntegerInfo(3), 23 IntegerInfo(3),
24 FloatInfo(4), 24 FloatInfo(4),
25 LongInfo(5), 25 LongInfo(5),
26 DoubleInfo(6), 26 DoubleInfo(6),
27 ClassInfo(7) { 27 ClassInfo(7) {
28 @Override 28 @Override
29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
31 gatherIndexTree(indices, editor, accessor.getNameIndex()); 31 gatherIndexTree(indices, editor, accessor.getNameIndex());
32 } 32 }
33 33
34 @Override 34 @Override
35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); 37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
38 } 38 }
39 39
40 @Override 40 @Override
41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); 42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); 43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); 44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
45 } 45 }
46 }, 46 },
47 StringInfo(8) { 47 StringInfo(8) {
48 @Override 48 @Override
49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
51 gatherIndexTree(indices, editor, accessor.getStringIndex()); 51 gatherIndexTree(indices, editor, accessor.getStringIndex());
52 } 52 }
53 53
54 @Override 54 @Override
55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); 57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex()));
58 } 58 }
59 59
60 @Override 60 @Override
61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); 62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); 63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex());
64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); 64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
65 } 65 }
66 }, 66 },
67 FieldRefInfo(9) { 67 FieldRefInfo(9) {
68 @Override 68 @Override
69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
71 gatherIndexTree(indices, editor, accessor.getClassIndex()); 71 gatherIndexTree(indices, editor, accessor.getClassIndex());
72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); 72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
73 } 73 }
74 74
75 @Override 75 @Override
76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); 78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex()));
79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); 79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
80 } 80 }
81 81
82 @Override 82 @Override
83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); 84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); 85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex());
86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); 86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); 87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
88 } 88 }
89 }, 89 },
90 // same as FieldRefInfo 90 // same as FieldRefInfo
91 MethodRefInfo(10) { 91 MethodRefInfo(10) {
92 @Override 92 @Override
93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
94 FieldRefInfo.gatherIndexTree(indices, editor, entry); 94 FieldRefInfo.gatherIndexTree(indices, editor, entry);
95 } 95 }
96 96
97 @Override 97 @Override
98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
99 FieldRefInfo.remapIndices(map, entry); 99 FieldRefInfo.remapIndices(map, entry);
100 } 100 }
101 101
102 @Override 102 @Override
103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
104 return FieldRefInfo.subIndicesAreValid(entry, pool); 104 return FieldRefInfo.subIndicesAreValid(entry, pool);
105 } 105 }
106 }, 106 },
107 // same as FieldRefInfo 107 // same as FieldRefInfo
108 InterfaceMethodRefInfo(11) { 108 InterfaceMethodRefInfo(11) {
109 @Override 109 @Override
110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
111 FieldRefInfo.gatherIndexTree(indices, editor, entry); 111 FieldRefInfo.gatherIndexTree(indices, editor, entry);
112 } 112 }
113 113
114 @Override 114 @Override
115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
116 FieldRefInfo.remapIndices(map, entry); 116 FieldRefInfo.remapIndices(map, entry);
117 } 117 }
118 118
119 @Override 119 @Override
120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
121 return FieldRefInfo.subIndicesAreValid(entry, pool); 121 return FieldRefInfo.subIndicesAreValid(entry, pool);
122 } 122 }
123 }, 123 },
124 NameAndTypeInfo(12) { 124 NameAndTypeInfo(12) {
125 @Override 125 @Override
126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
128 gatherIndexTree(indices, editor, accessor.getNameIndex()); 128 gatherIndexTree(indices, editor, accessor.getNameIndex());
129 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 129 gatherIndexTree(indices, editor, accessor.getTypeIndex());
130 } 130 }
131 131
132 @Override 132 @Override
133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); 135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
137 } 137 }
138 138
139 @Override 139 @Override
140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); 141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); 142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
145 } 145 }
146 }, 146 },
147 MethodHandleInfo(15) { 147 MethodHandleInfo(15) {
148 @Override 148 @Override
149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
151 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 151 gatherIndexTree(indices, editor, accessor.getTypeIndex());
152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); 152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex());
153 } 153 }
154 154
155 @Override 155 @Override
156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); 159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex()));
160 } 160 }
161 161
162 @Override 162 @Override
163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); 164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); 166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex());
167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); 167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
168 } 168 }
169 }, 169 },
170 MethodTypeInfo(16) { 170 MethodTypeInfo(16) {
171 @Override 171 @Override
172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
174 gatherIndexTree(indices, editor, accessor.getTypeIndex()); 174 gatherIndexTree(indices, editor, accessor.getTypeIndex());
175 } 175 }
176 176
177 @Override 177 @Override
178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); 180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
181 } 181 }
182 182
183 @Override 183 @Override
184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); 185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); 186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); 187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
188 } 188 }
189 }, 189 },
190 InvokeDynamicInfo(18) { 190 InvokeDynamicInfo(18) {
191 @Override 191 @Override
192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); 194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex());
195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); 195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
196 } 196 }
197 197
198 @Override 198 @Override
199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); 201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex()));
202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); 202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
203 } 203 }
204 204
205 @Override 205 @Override
206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); 207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); 208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex());
209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); 209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); 210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
211 } 211 }
212 }; 212 };
213 213
214 private static Map<Integer, InfoType> types; 214 private static Map<Integer, InfoType> types;
215 215
216 static { 216 static {
217 types = Maps.newTreeMap(); 217 types = Maps.newTreeMap();
218 for (InfoType type : values()) { 218 for (InfoType type : values()) {
219 types.put(type.getTag(), type); 219 types.put(type.getTag(), type);
220 } 220 }
221 } 221 }
222 222
223 private int tag; 223 private int tag;
224 224
225 InfoType(int tag) { 225 InfoType(int tag) {
226 this.tag = tag; 226 this.tag = tag;
227 } 227 }
228 228
229 public int getTag() { 229 public static InfoType getByTag(int tag) {
230 return this.tag; 230 return types.get(tag);
231 } 231 }
232 232
233 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { 233 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) {
234 // by default, do nothing 234 // add own index
235 } 235 indices.add(index);
236 236
237 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { 237 // recurse
238 // by default, do nothing 238 ConstInfoAccessor entry = editor.getItem(index);
239 } 239 entry.getType().gatherIndexTree(indices, editor, entry);
240 240 }
241 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { 241
242 // by default, everything is good 242 private static int remapIndex(Map<Integer, Integer> map, int index) {
243 return true; 243 Integer newIndex = map.get(index);
244 } 244 if (newIndex == null) {
245 245 newIndex = index;
246 public static InfoType getByTag(int tag) { 246 }
247 return types.get(tag); 247 return newIndex;
248 } 248 }
249 249
250 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { 250 public int getTag() {
251 // add own index 251 return this.tag;
252 indices.add(index); 252 }
253 253
254 // recurse 254 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
255 ConstInfoAccessor entry = editor.getItem(index); 255 // by default, do nothing
256 entry.getType().gatherIndexTree(indices, editor, entry); 256 }
257 } 257
258 258 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
259 private static int remapIndex(Map<Integer, Integer> map, int index) { 259 // by default, do nothing
260 Integer newIndex = map.get(index); 260 }
261 if (newIndex == null) { 261
262 newIndex = index; 262 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
263 } 263 // by default, everything is good
264 return newIndex; 264 return true;
265 } 265 }
266} 266}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
index eb70c23..5f8be90 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -8,13 +8,10 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.Collection;
16import java.util.List;
17
18import cuchaz.enigma.analysis.JarIndex; 15import cuchaz.enigma.analysis.JarIndex;
19import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.mapping.*;
20import javassist.ClassPool; 17import javassist.ClassPool;
@@ -22,126 +19,126 @@ import javassist.CtClass;
22import javassist.NotFoundException; 19import javassist.NotFoundException;
23import javassist.bytecode.*; 20import javassist.bytecode.*;
24 21
22import java.util.Collection;
23import java.util.List;
24
25public class InnerClassWriter { 25public class InnerClassWriter {
26 26
27 private JarIndex index; 27 private JarIndex index;
28 private Translator deobfuscatorTranslator; 28 private Translator deobfuscatorTranslator;
29 29
30 public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { 30 public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) {
31 this.index = index; 31 this.index = index;
32 this.deobfuscatorTranslator = deobfuscatorTranslator; 32 this.deobfuscatorTranslator = deobfuscatorTranslator;
33 } 33 }
34 34
35 public void write(CtClass c) { 35 // FIXME: modiffier is not applied to inner class
36 36 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
37 // don't change anything if there's already an attribute there 37 ClassPool pool = c.getClassPool();
38 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 38 for (int i = 0; i < attr.tableLength(); i++) {
39 if (oldAttr != null) { 39
40 // bail! 40 String innerName = attr.innerClass(i);
41 return; 41 // get the inner class full name (which has already been translated)
42 } 42 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
43 43 try {
44 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); 44 CtClass innerClass = pool.get(innerName);
45 List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry); 45 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
46 46 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
47 boolean isInnerClass = obfClassChain.size() > 1; 47 ClassRenamer.applyModifier(innerClass, modifier);
48 if (isInnerClass) { 48 } catch (NotFoundException e) {
49 49 // This shouldn't be possible in theory
50 // it's an inner class, rename it to the fully qualified name 50 //e.printStackTrace();
51 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); 51 }
52 52 }
53 BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); 53 }
54 if (caller != null) { 54
55 55 public void write(CtClass c) {
56 // write the enclosing method attribute 56
57 if (caller.getName().equals("<clinit>")) { 57 // don't change anything if there's already an attribute there
58 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); 58 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
59 } else { 59 if (oldAttr != null) {
60 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); 60 // bail!
61 } 61 return;
62 } 62 }
63 } 63
64 64 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
65 // does this class have any inner classes? 65 List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry);
66 Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); 66
67 67 boolean isInnerClass = obfClassChain.size() > 1;
68 if (isInnerClass || !obfInnerClassEntries.isEmpty()) { 68 if (isInnerClass) {
69 69
70 // create an inner class attribute 70 // it's an inner class, rename it to the fully qualified name
71 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); 71 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName());
72 c.getClassFile().addAttribute(attr); 72
73 73 BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry);
74 // write the ancestry, but not the outermost class 74 if (caller != null) {
75 for (int i = 1; i < obfClassChain.size(); i++) { 75
76 ClassEntry obfInnerClassEntry = obfClassChain.get(i); 76 // write the enclosing method attribute
77 writeInnerClass(attr, obfClassChain, obfInnerClassEntry); 77 if (caller.getName().equals("<clinit>")) {
78 78 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName()));
79 // update references to use the fully qualified inner class name 79 } else {
80 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); 80 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString()));
81 } 81 }
82 82 }
83 // write the inner classes 83 }
84 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { 84
85 85 // does this class have any inner classes?
86 // extend the class chain 86 Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry);
87 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); 87
88 extendedObfClassChain.add(obfInnerClassEntry); 88 if (isInnerClass || !obfInnerClassEntries.isEmpty()) {
89 89
90 writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); 90 // create an inner class attribute
91 91 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool());
92 // update references to use the fully qualified inner class name 92 c.getClassFile().addAttribute(attr);
93 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); 93
94 } 94 // write the ancestry, but not the outermost class
95 } 95 for (int i = 1; i < obfClassChain.size(); i++) {
96 } 96 ClassEntry obfInnerClassEntry = obfClassChain.get(i);
97 97 writeInnerClass(attr, obfClassChain, obfInnerClassEntry);
98 // FIXME: modiffier is not applied to inner class 98
99 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) 99 // update references to use the fully qualified inner class name
100 { 100 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName());
101 ClassPool pool = c.getClassPool(); 101 }
102 for (int i = 0; i < attr.tableLength(); i++) { 102
103 103 // write the inner classes
104 String innerName = attr.innerClass(i); 104 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) {
105 // get the inner class full name (which has already been translated) 105
106 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); 106 // extend the class chain
107 try 107 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain);
108 { 108 extendedObfClassChain.add(obfInnerClassEntry);
109 CtClass innerClass = pool.get(innerName); 109
110 Mappings.EntryModifier modifier = translator.getModifier(classEntry); 110 writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry);
111 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) 111
112 ClassRenamer.applyModifier(innerClass, modifier); 112 // update references to use the fully qualified inner class name
113 } catch (NotFoundException e) 113 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName());
114 { 114 }
115 // This shouldn't be possible in theory 115 }
116 //e.printStackTrace(); 116 }
117 } 117
118 } 118 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
119 } 119
120 120 // get the new inner class name
121 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { 121 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain);
122 122 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry();
123 // get the new inner class name 123
124 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); 124 // here's what the JVM spec says about the InnerClasses attribute
125 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); 125 // append(inner, parent, 0 if anonymous else simple name, flags);
126 126
127 // here's what the JVM spec says about the InnerClasses attribute 127 // update the attribute with this inner class
128 // append(inner, parent, 0 if anonymous else simple name, flags); 128 ConstPool constPool = attr.getConstPool();
129 129 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName());
130 // update the attribute with this inner class 130 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName());
131 ConstPool constPool = attr.getConstPool(); 131 int innerClassNameIndex = 0;
132 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); 132 int accessFlags = AccessFlag.PUBLIC;
133 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); 133 // TODO: need to figure out if we can put static or not
134 int innerClassNameIndex = 0; 134 if (!this.index.isAnonymousClass(obfClassEntry)) {
135 int accessFlags = AccessFlag.PUBLIC; 135 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName());
136 // TODO: need to figure out if we can put static or not 136 }
137 if (!this.index.isAnonymousClass(obfClassEntry)) { 137
138 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); 138 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
139 }
140
141 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
142 139
143 /* DEBUG 140 /* DEBUG
144 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", 141 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)",
145 obfClassEntry, 142 obfClassEntry,
146 attr.innerClass(attr.tableLength() - 1), 143 attr.innerClass(attr.tableLength() - 1),
147 attr.outerClass(attr.tableLength() - 1), 144 attr.outerClass(attr.tableLength() - 1),
@@ -150,5 +147,5 @@ public class InnerClassWriter {
150 obfClassEntry.getName() 147 obfClassEntry.getName()
151 )); 148 ));
152 */ 149 */
153 } 150 }
154} 151}
diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
index 24b5f36..8909d81 100644
--- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
13import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
@@ -15,131 +16,129 @@ import javassist.CtBehavior;
15import javassist.CtClass; 16import javassist.CtClass;
16import javassist.bytecode.*; 17import javassist.bytecode.*;
17 18
18
19public class LocalVariableRenamer { 19public class LocalVariableRenamer {
20 20
21 private Translator translator; 21 private Translator translator;
22 22
23 public LocalVariableRenamer(Translator translator) { 23 public LocalVariableRenamer(Translator translator) {
24 this.translator = translator; 24 this.translator = translator;
25 } 25 }
26 26
27 public void rename(CtClass c) { 27 public void rename(CtClass c) {
28 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 28 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
29 29
30 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now 30 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
31 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); 31 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
32 if (codeAttribute == null) { 32 if (codeAttribute == null) {
33 continue; 33 continue;
34 } 34 }
35 35
36 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 36 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
37 ConstPool constants = c.getClassFile().getConstPool(); 37 ConstPool constants = c.getClassFile().getConstPool();
38 38
39 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); 39 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
40 if (table != null) { 40 if (table != null) {
41 renameLVT(behaviorEntry, constants, table); 41 renameLVT(behaviorEntry, constants, table);
42 } 42 }
43 43
44 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); 44 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
45 if (typeTable != null) { 45 if (typeTable != null) {
46 renameLVTT(typeTable, table); 46 renameLVTT(typeTable, table);
47 } 47 }
48 } 48 }
49 } 49 }
50 50
51 // DEBUG 51 // DEBUG
52 @SuppressWarnings("unused") 52 @SuppressWarnings("unused")
53 private void dumpTable(LocalVariableAttribute table) { 53 private void dumpTable(LocalVariableAttribute table) {
54 for (int i = 0; i < table.tableLength(); i++) { 54 for (int i = 0; i < table.tableLength(); i++) {
55 System.out.println(String.format("\t%d (%d): %s %s", 55 System.out.println(String.format("\t%d (%d): %s %s",
56 i, table.index(i), table.variableName(i), table.descriptor(i) 56 i, table.index(i), table.variableName(i), table.descriptor(i)
57 )); 57 ));
58 } 58 }
59 } 59 }
60 60
61 private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { 61 private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) {
62 62
63 // skip empty tables 63 // skip empty tables
64 if (table.tableLength() <= 0) { 64 if (table.tableLength() <= 0) {
65 return; 65 return;
66 } 66 }
67 67
68 // where do we start counting variables? 68 // where do we start counting variables?
69 int starti = 0; 69 int starti = 0;
70 if (table.variableName(0).equals("this")) { 70 if (table.variableName(0).equals("this")) {
71 // skip the "this" variable 71 // skip the "this" variable
72 starti = 1; 72 starti = 1;
73 } 73 }
74 74
75 // rename method arguments first 75 // rename method arguments first
76 int numArgs = 0; 76 int numArgs = 0;
77 if (behaviorEntry.getSignature() != null) { 77 if (behaviorEntry.getSignature() != null) {
78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); 78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
79 79
80 boolean isNestedClassConstructor = false; 80 boolean isNestedClassConstructor = false;
81 81
82 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! 82 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
83 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) 83 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
84 { 84 // Get the first arg type
85 // Get the first arg type 85 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
86 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); 86
87 87 // 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
88 // 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 88 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
89 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { 89 isNestedClassConstructor = true;
90 isNestedClassConstructor = true; 90 numArgs--;
91 numArgs--; 91 }
92 } 92 }
93 } 93
94 94 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
95 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { 95 int argi = i - starti;
96 int argi = i - starti; 96 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
97 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); 97 if (argName == null) {
98 if (argName == null) { 98 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi);
99 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi); 99 // Unfortunately each of these have different name getters, so they have different code paths
100 // Unfortunately each of these have different name getters, so they have different code paths 100 if (argType.isPrimitive()) {
101 if (argType.isPrimitive()) { 101 Type.Primitive argCls = argType.getPrimitive();
102 Type.Primitive argCls = argType.getPrimitive(); 102 argName = "a" + argCls.name() + (argi + 1);
103 argName = "a" + argCls.name() + (argi + 1); 103 } else if (argType.isArray()) {
104 } else if (argType.isArray()) { 104 // List types would require this whole block again, so just go with aListx
105 // List types would require this whole block again, so just go with aListx 105 argName = "aList" + (argi + 1);
106 argName = "aList" + (argi + 1); 106 } else if (argType.isClass()) {
107 } else if (argType.isClass()) { 107 ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry());
108 ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); 108 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1);
109 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1); 109 } else {
110 } else { 110 argName = "a" + (argi + 1);
111 argName = "a" + (argi + 1); 111 }
112 } 112 }
113 } 113 renameVariable(table, i, constants.addUtf8Info(argName));
114 renameVariable(table, i, constants.addUtf8Info(argName)); 114 }
115 } 115 }
116 } 116
117 117 // then rename the rest of the args, if any
118 // then rename the rest of the args, if any 118 for (int i = starti + numArgs; i < table.tableLength(); i++) {
119 for (int i = starti + numArgs; i < table.tableLength(); i++) { 119 int firstIndex = table.index(starti + numArgs);
120 int firstIndex = table.index(starti + numArgs); 120 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
121 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); 121 }
122 } 122 }
123 } 123
124 124 private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
125 private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { 125 // rename args to the same names as in the LVT
126 // rename args to the same names as in the LVT 126 for (int i = 0; i < typeTable.tableLength(); i++) {
127 for (int i = 0; i < typeTable.tableLength(); i++) { 127 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
128 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); 128 }
129 } 129 }
130 } 130
131 131 private void renameVariable(LocalVariableAttribute table, int i, int stringId) {
132 private void renameVariable(LocalVariableAttribute table, int i, int stringId) { 132 // based off of LocalVariableAttribute.nameIndex()
133 // based off of LocalVariableAttribute.nameIndex() 133 ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
134 ByteArray.write16bit(stringId, table.get(), i * 10 + 6); 134 }
135 } 135
136 136 private int getNameIndex(LocalVariableAttribute table, int index) {
137 private int getNameIndex(LocalVariableAttribute table, int index) { 137 for (int i = 0; i < table.tableLength(); i++) {
138 for (int i = 0; i < table.tableLength(); i++) { 138 if (table.index(i) == index) {
139 if (table.index(i) == index) { 139 return table.nameIndex(i);
140 return table.nameIndex(i); 140 }
141 } 141 }
142 } 142 return 0;
143 return 0; 143 }
144 }
145} 144}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
index 28ad04a..d63572e 100644
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
+++ b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java
@@ -8,10 +8,8 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12 11
13import java.util.ArrayList; 12package cuchaz.enigma.bytecode;
14import java.util.List;
15 13
16import cuchaz.enigma.mapping.*; 14import cuchaz.enigma.mapping.*;
17import javassist.CtBehavior; 15import javassist.CtBehavior;
@@ -19,48 +17,51 @@ import javassist.CtClass;
19import javassist.bytecode.CodeAttribute; 17import javassist.bytecode.CodeAttribute;
20import javassist.bytecode.LocalVariableAttribute; 18import javassist.bytecode.LocalVariableAttribute;
21 19
20import java.util.ArrayList;
21import java.util.List;
22
22public class MethodParameterWriter { 23public class MethodParameterWriter {
23 24
24 private Translator translator; 25 private Translator translator;
25 26
26 public MethodParameterWriter(Translator translator) { 27 public MethodParameterWriter(Translator translator) {
27 this.translator = translator; 28 this.translator = translator;
28 } 29 }
29 30
30 public void writeMethodArguments(CtClass c) { 31 public void writeMethodArguments(CtClass c) {
31 32
32 // Procyon will read method arguments from the "MethodParameters" attribute, so write those 33 // Procyon will read method arguments from the "MethodParameters" attribute, so write those
33 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 34 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
34 35
35 // if there's a local variable table here, don't write a MethodParameters attribute 36 // if there's a local variable table here, don't write a MethodParameters attribute
36 // let the local variable writer deal with it instead 37 // let the local variable writer deal with it instead
37 // procyon starts doing really weird things if we give it both attributes 38 // procyon starts doing really weird things if we give it both attributes
38 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); 39 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
39 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { 40 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
40 continue; 41 continue;
41 } 42 }
42 43
43 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 44 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
44 45
45 // get the number of arguments 46 // get the number of arguments
46 Signature signature = behaviorEntry.getSignature(); 47 Signature signature = behaviorEntry.getSignature();
47 if (signature == null) { 48 if (signature == null) {
48 // static initializers have no signatures, or arguments 49 // static initializers have no signatures, or arguments
49 continue; 50 continue;
50 } 51 }
51 int numParams = signature.getArgumentTypes().size(); 52 int numParams = signature.getArgumentTypes().size();
52 if (numParams <= 0) { 53 if (numParams <= 0) {
53 continue; 54 continue;
54 } 55 }
55 56
56 // get the list of argument names 57 // get the list of argument names
57 List<String> names = new ArrayList<>(numParams); 58 List<String> names = new ArrayList<>(numParams);
58 for (int i = 0; i < numParams; i++) { 59 for (int i = 0; i < numParams; i++) {
59 names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); 60 names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, "")));
60 } 61 }
61 62
62 // save the mappings to the class 63 // save the mappings to the class
63 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); 64 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names);
64 } 65 }
65 } 66 }
66} 67}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
index bace3a0..3f819ab 100644
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
+++ b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
@@ -8,79 +8,80 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
12 13
14import javassist.bytecode.AttributeInfo;
15import javassist.bytecode.ConstPool;
16import javassist.bytecode.MethodInfo;
17
13import java.io.ByteArrayOutputStream; 18import java.io.ByteArrayOutputStream;
14import java.io.DataOutputStream; 19import java.io.DataOutputStream;
15import java.io.IOException; 20import java.io.IOException;
16import java.util.ArrayList; 21import java.util.ArrayList;
17import java.util.List; 22import java.util.List;
18 23
19import javassist.bytecode.AttributeInfo;
20import javassist.bytecode.ConstPool;
21import javassist.bytecode.MethodInfo;
22
23public class MethodParametersAttribute extends AttributeInfo { 24public class MethodParametersAttribute extends AttributeInfo {
24 25
25 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) { 26 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) {
26 super(pool, "MethodParameters", writeStruct(parameterNameIndices)); 27 super(pool, "MethodParameters", writeStruct(parameterNameIndices));
27 } 28 }
28 29
29 public static void updateClass(MethodInfo info, List<String> names) { 30 public static void updateClass(MethodInfo info, List<String> names) {
30 31
31 // add the names to the class const pool 32 // add the names to the class const pool
32 ConstPool constPool = info.getConstPool(); 33 ConstPool constPool = info.getConstPool();
33 List<Integer> parameterNameIndices = new ArrayList<>(); 34 List<Integer> parameterNameIndices = new ArrayList<>();
34 for (String name : names) { 35 for (String name : names) {
35 if (name != null) { 36 if (name != null) {
36 parameterNameIndices.add(constPool.addUtf8Info(name)); 37 parameterNameIndices.add(constPool.addUtf8Info(name));
37 } else { 38 } else {
38 parameterNameIndices.add(0); 39 parameterNameIndices.add(0);
39 } 40 }
40 } 41 }
41 42
42 // add the attribute to the method 43 // add the attribute to the method
43 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); 44 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices));
44 } 45 }
45 46
46 private static byte[] writeStruct(List<Integer> parameterNameIndices) { 47 private static byte[] writeStruct(List<Integer> parameterNameIndices) {
47 // JVM 8 Spec says the struct looks like this: 48 // JVM 8 Spec says the struct looks like this:
48 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 49 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24
49 // uint8 num_params 50 // uint8 num_params
50 // for each param: 51 // for each param:
51 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry 52 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
52 // uint16 access_flags -> don't care, just set to 0 53 // uint16 access_flags -> don't care, just set to 0
53 54
54 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 55 ByteArrayOutputStream buf = new ByteArrayOutputStream();
55 DataOutputStream out = new DataOutputStream(buf); 56 DataOutputStream out = new DataOutputStream(buf);
56 57
57 // NOTE: java hates unsigned integers, so we have to be careful here 58 // NOTE: java hates unsigned integers, so we have to be careful here
58 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument 59 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
59 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte 60 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
60 // if the int is out of range, the byte stream won't look the way we want and weird things will happen 61 // if the int is out of range, the byte stream won't look the way we want and weird things will happen
61 final int SIZEOF_UINT8 = 1; 62 final int SIZEOF_UINT8 = 1;
62 final int SIZEOF_UINT16 = 2; 63 final int SIZEOF_UINT16 = 2;
63 final int MAX_UINT8 = (1 << 8) - 1; 64 final int MAX_UINT8 = (1 << 8) - 1;
64 final int MAX_UINT16 = (1 << 16) - 1; 65 final int MAX_UINT16 = (1 << 16) - 1;
65 66
66 try { 67 try {
67 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); 68 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8);
68 out.writeByte(parameterNameIndices.size()); 69 out.writeByte(parameterNameIndices.size());
69 70
70 for (Integer index : parameterNameIndices) { 71 for (Integer index : parameterNameIndices) {
71 assert (index >= 0 && index <= MAX_UINT16); 72 assert (index >= 0 && index <= MAX_UINT16);
72 out.writeShort(index); 73 out.writeShort(index);
73 74
74 // just write 0 for the access flags 75 // just write 0 for the access flags
75 out.writeShort(0); 76 out.writeShort(0);
76 } 77 }
77 78
78 out.close(); 79 out.close();
79 byte[] data = buf.toByteArray(); 80 byte[] data = buf.toByteArray();
80 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); 81 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16));
81 return data; 82 return data;
82 } catch (IOException ex) { 83 } catch (IOException ex) {
83 throw new Error(ex); 84 throw new Error(ex);
84 } 85 }
85 } 86 }
86} 87}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
index 66f2283..eaa6e90 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
@@ -8,48 +8,49 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class ClassInfoAccessor { 16public class ClassInfoAccessor {
16 17
17 private Object item; 18 private static Class<?> clazz;
18 19 private static Field nameIndex;
19 private static Class<?> clazz; 20
20 private static Field nameIndex; 21 static {
21 22 try {
22 public ClassInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.ClassInfo");
23 this.item = item; 24 nameIndex = clazz.getDeclaredField("name");
24 } 25 nameIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getNameIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) nameIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public ClassInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setNameIndex(int val) { 35 }
35 try { 36
36 nameIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getNameIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) nameIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.ClassInfo"); 49 public void setNameIndex(int val) {
49 nameIndex = clazz.getDeclaredField("name"); 50 try {
50 nameIndex.setAccessible(true); 51 nameIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55} 56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
index aa363d2..27d991a 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
@@ -8,122 +8,117 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode.accessors;
12 11
13import java.io.ByteArrayInputStream; 12package cuchaz.enigma.bytecode.accessors;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.io.OutputStreamWriter;
19import java.io.PrintWriter;
20import java.lang.reflect.Field;
21import java.lang.reflect.Method;
22 13
23import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
24import cuchaz.enigma.bytecode.InfoType; 15import cuchaz.enigma.bytecode.InfoType;
25 16
17import java.io.*;
18import java.lang.reflect.Field;
19import java.lang.reflect.Method;
20
26public class ConstInfoAccessor { 21public class ConstInfoAccessor {
27 22
28 private static Class<?> clazz; 23 private static Class<?> clazz;
29 private static Field index; 24 private static Field index;
30 private static Method getTag; 25 private static Method getTag;
31 26
32 private Object item; 27 static {
33 28 try {
34 public ConstInfoAccessor(Object item) { 29 clazz = Class.forName("javassist.bytecode.ConstInfo");
35 if (item == null) { 30 index = clazz.getDeclaredField("index");
36 throw new IllegalArgumentException("item cannot be null!"); 31 index.setAccessible(true);
37 } 32 getTag = clazz.getMethod("getTag");
38 this.item = item; 33 getTag.setAccessible(true);
39 } 34 } catch (Exception ex) {
40 35 throw new Error(ex);
41 public Object getItem() { 36 }
42 return this.item; 37 }
43 } 38
44 39 private Object item;
45 public int getIndex() { 40
46 try { 41 public ConstInfoAccessor(Object item) {
47 return (Integer) index.get(this.item); 42 if (item == null) {
48 } catch (Exception ex) { 43 throw new IllegalArgumentException("item cannot be null!");
49 throw new Error(ex); 44 }
50 } 45 this.item = item;
51 } 46 }
52 47
53 public int getTag() { 48 public Object getItem() {
54 try { 49 return this.item;
55 return (Integer) getTag.invoke(this.item); 50 }
56 } catch (Exception ex) { 51
57 throw new Error(ex); 52 public int getIndex() {
58 } 53 try {
59 } 54 return (Integer) index.get(this.item);
60 55 } catch (Exception ex) {
61 public ConstInfoAccessor copy() { 56 throw new Error(ex);
62 return new ConstInfoAccessor(copyItem()); 57 }
63 } 58 }
64 59
65 public Object copyItem() { 60 public int getTag() {
66 // I don't know of a simpler way to copy one of these silly things... 61 try {
67 try { 62 return (Integer) getTag.invoke(this.item);
68 // serialize the item 63 } catch (Exception ex) {
69 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 64 throw new Error(ex);
70 DataOutputStream out = new DataOutputStream(buf); 65 }
71 write(out); 66 }
72 67
73 // deserialize the item 68 public ConstInfoAccessor copy() {
74 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); 69 return new ConstInfoAccessor(copyItem());
75 Object item = new ConstInfoAccessor(in).getItem(); 70 }
76 in.close(); 71
77 72 public Object copyItem() {
78 return item; 73 // I don't know of a simpler way to copy one of these silly things...
79 } catch (Exception ex) { 74 try {
80 throw new Error(ex); 75 // serialize the item
81 } 76 ByteArrayOutputStream buf = new ByteArrayOutputStream();
82 } 77 DataOutputStream out = new DataOutputStream(buf);
83 78 write(out);
84 public void write(DataOutputStream out) throws IOException { 79
85 try { 80 // deserialize the item
86 out.writeUTF(this.item.getClass().getName()); 81 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray()));
87 out.writeInt(getIndex()); 82 Object item = new ConstInfoAccessor(in).getItem();
88 83 in.close();
89 Method method = this.item.getClass().getMethod("write", DataOutputStream.class); 84
90 method.setAccessible(true); 85 return item;
91 method.invoke(this.item, out); 86 } catch (Exception ex) {
92 } catch (IOException ex) { 87 throw new Error(ex);
93 throw ex; 88 }
94 } catch (Exception ex) { 89 }
95 throw new Error(ex); 90
96 } 91 public void write(DataOutputStream out) throws IOException {
97 } 92 try {
98 93 out.writeUTF(this.item.getClass().getName());
99 @Override 94 out.writeInt(getIndex());
100 public String toString() { 95
101 try { 96 Method method = this.item.getClass().getMethod("write", DataOutputStream.class);
102 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 97 method.setAccessible(true);
103 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); 98 method.invoke(this.item, out);
104 Method print = this.item.getClass().getMethod("print", PrintWriter.class); 99 } catch (IOException ex) {
105 print.setAccessible(true); 100 throw ex;
106 print.invoke(this.item, out); 101 } catch (Exception ex) {
107 out.close(); 102 throw new Error(ex);
108 return buf.toString("UTF-8").replace("\n", ""); 103 }
109 } catch (Exception ex) { 104 }
110 throw new Error(ex); 105
111 } 106 @Override
112 } 107 public String toString() {
113 108 try {
114 public InfoType getType() { 109 ByteArrayOutputStream buf = new ByteArrayOutputStream();
115 return InfoType.getByTag(getTag()); 110 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8));
116 } 111 Method print = this.item.getClass().getMethod("print", PrintWriter.class);
117 112 print.setAccessible(true);
118 static { 113 print.invoke(this.item, out);
119 try { 114 out.close();
120 clazz = Class.forName("javassist.bytecode.ConstInfo"); 115 return buf.toString("UTF-8").replace("\n", "");
121 index = clazz.getDeclaredField("index"); 116 } catch (Exception ex) {
122 index.setAccessible(true); 117 throw new Error(ex);
123 getTag = clazz.getMethod("getTag"); 118 }
124 getTag.setAccessible(true); 119 }
125 } catch (Exception ex) { 120
126 throw new Error(ex); 121 public InfoType getType() {
127 } 122 return InfoType.getByTag(getTag());
128 } 123 }
129} 124}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
index 69aee16..aef3532 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
@@ -8,68 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class InvokeDynamicInfoAccessor { 16public class InvokeDynamicInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field bootstrapIndex; 19 private static Field bootstrapIndex;
19 private static Field nameAndTypeIndex; 20 private static Field nameAndTypeIndex;
20
21 21
22 private Object item; 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 }
23 33
24 public InvokeDynamicInfoAccessor(Object item) { 34 private Object item;
25 this.item = item;
26 }
27 35
28 public int getBootstrapIndex() { 36 public InvokeDynamicInfoAccessor(Object item) {
29 try { 37 this.item = item;
30 return (Integer) bootstrapIndex.get(this.item); 38 }
31 } catch (Exception ex) {
32 throw new Error(ex);
33 }
34 }
35 39
36 public void setBootstrapIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
37 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 bootstrapIndex.set(this.item, val); 42 }
39 } catch (Exception ex) {
40 throw new Error(ex);
41 }
42 }
43 43
44 public int getNameAndTypeIndex() { 44 public int getBootstrapIndex() {
45 try { 45 try {
46 return (Integer) nameAndTypeIndex.get(this.item); 46 return (Integer) bootstrapIndex.get(this.item);
47 } catch (Exception ex) { 47 } catch (Exception ex) {
48 throw new Error(ex); 48 throw new Error(ex);
49 } 49 }
50 } 50 }
51 51
52 public void setNameAndTypeIndex(int val) { 52 public void setBootstrapIndex(int val) {
53 try { 53 try {
54 nameAndTypeIndex.set(this.item, val); 54 bootstrapIndex.set(this.item, val);
55 } catch (Exception ex) { 55 } catch (Exception ex) {
56 throw new Error(ex); 56 throw new Error(ex);
57 } 57 }
58 } 58 }
59 59
60 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getNameAndTypeIndex() {
61 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
62 } 62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
63 67
64 static { 68 public void setNameAndTypeIndex(int val) {
65 try { 69 try {
66 clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); 70 nameAndTypeIndex.set(this.item, val);
67 bootstrapIndex = clazz.getDeclaredField("bootstrap"); 71 } catch (Exception ex) {
68 bootstrapIndex.setAccessible(true); 72 throw new Error(ex);
69 nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); 73 }
70 nameAndTypeIndex.setAccessible(true); 74 }
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75} 75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
index 0e0297b..058bb45 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MemberRefInfoAccessor { 16public class MemberRefInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field classIndex; 19 private static Field classIndex;
19 private static Field nameAndTypeIndex; 20 private static Field nameAndTypeIndex;
20 21
21 private Object item; 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 }
22 33
23 public MemberRefInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getClassIndex() { 36 public MemberRefInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) classIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setClassIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 classIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getNameAndTypeIndex() { 44 public int getClassIndex() {
44 try { 45 try {
45 return (Integer) nameAndTypeIndex.get(this.item); 46 return (Integer) classIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setNameAndTypeIndex(int val) { 52 public void setClassIndex(int val) {
52 try { 53 try {
53 nameAndTypeIndex.set(this.item, val); 54 classIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getNameAndTypeIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setNameAndTypeIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.MemberrefInfo"); 70 nameAndTypeIndex.set(this.item, val);
66 classIndex = clazz.getDeclaredField("classIndex"); 71 } catch (Exception ex) {
67 classIndex.setAccessible(true); 72 throw new Error(ex);
68 nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); 73 }
69 nameAndTypeIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
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
index 9a7dd69..985e792 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MethodHandleInfoAccessor { 16public class MethodHandleInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field kindIndex; 19 private static Field kindIndex;
19 private static Field indexIndex; 20 private static Field indexIndex;
20 21
21 private Object item; 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 }
22 33
23 public MethodHandleInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getTypeIndex() { 36 public MethodHandleInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) kindIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setTypeIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 kindIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getMethodRefIndex() { 44 public int getTypeIndex() {
44 try { 45 try {
45 return (Integer) indexIndex.get(this.item); 46 return (Integer) kindIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setMethodRefIndex(int val) { 52 public void setTypeIndex(int val) {
52 try { 53 try {
53 indexIndex.set(this.item, val); 54 kindIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getMethodRefIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) indexIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setMethodRefIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); 70 indexIndex.set(this.item, val);
66 kindIndex = clazz.getDeclaredField("refKind"); 71 } catch (Exception ex) {
67 kindIndex.setAccessible(true); 72 throw new Error(ex);
68 indexIndex = clazz.getDeclaredField("refIndex"); 73 }
69 indexIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
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
index 5ec9c3b..10b0cb0 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
@@ -8,49 +8,50 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class MethodTypeInfoAccessor { 16public class MethodTypeInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field descriptorIndex; 19 private static Field descriptorIndex;
19 20
20 private Object item; 21 static {
21 22 try {
22 public MethodTypeInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.MethodTypeInfo");
23 this.item = item; 24 descriptorIndex = clazz.getDeclaredField("descriptor");
24 } 25 descriptorIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getTypeIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) descriptorIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public MethodTypeInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setTypeIndex(int val) { 35 }
35 try { 36
36 descriptorIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getTypeIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) descriptorIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); 49 public void setTypeIndex(int val) {
49 descriptorIndex = clazz.getDeclaredField("descriptor"); 50 try {
50 descriptorIndex.setAccessible(true); 51 descriptorIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55 56
56} 57}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
index 95df37c..cc7fdbe 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
@@ -8,67 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class NameAndTypeInfoAccessor { 16public class NameAndTypeInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field nameIndex; 19 private static Field nameIndex;
19 private static Field typeIndex; 20 private static Field typeIndex;
20 21
21 private Object item; 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 }
22 33
23 public NameAndTypeInfoAccessor(Object item) { 34 private Object item;
24 this.item = item;
25 }
26 35
27 public int getNameIndex() { 36 public NameAndTypeInfoAccessor(Object item) {
28 try { 37 this.item = item;
29 return (Integer) nameIndex.get(this.item); 38 }
30 } catch (Exception ex) {
31 throw new Error(ex);
32 }
33 }
34 39
35 public void setNameIndex(int val) { 40 public static boolean isType(ConstInfoAccessor accessor) {
36 try { 41 return clazz.isAssignableFrom(accessor.getItem().getClass());
37 nameIndex.set(this.item, val); 42 }
38 } catch (Exception ex) {
39 throw new Error(ex);
40 }
41 }
42 43
43 public int getTypeIndex() { 44 public int getNameIndex() {
44 try { 45 try {
45 return (Integer) typeIndex.get(this.item); 46 return (Integer) nameIndex.get(this.item);
46 } catch (Exception ex) { 47 } catch (Exception ex) {
47 throw new Error(ex); 48 throw new Error(ex);
48 } 49 }
49 } 50 }
50 51
51 public void setTypeIndex(int val) { 52 public void setNameIndex(int val) {
52 try { 53 try {
53 typeIndex.set(this.item, val); 54 nameIndex.set(this.item, val);
54 } catch (Exception ex) { 55 } catch (Exception ex) {
55 throw new Error(ex); 56 throw new Error(ex);
56 } 57 }
57 } 58 }
58 59
59 public static boolean isType(ConstInfoAccessor accessor) { 60 public int getTypeIndex() {
60 return clazz.isAssignableFrom(accessor.getItem().getClass()); 61 try {
61 } 62 return (Integer) typeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
62 67
63 static { 68 public void setTypeIndex(int val) {
64 try { 69 try {
65 clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); 70 typeIndex.set(this.item, val);
66 nameIndex = clazz.getDeclaredField("memberName"); 71 } catch (Exception ex) {
67 nameIndex.setAccessible(true); 72 throw new Error(ex);
68 typeIndex = clazz.getDeclaredField("typeDescriptor"); 73 }
69 typeIndex.setAccessible(true); 74 }
70 } catch (Exception ex) {
71 throw new Error(ex);
72 }
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
index 1c55a44..5c68d4a 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
@@ -8,48 +8,49 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13import java.lang.reflect.Field; 14import java.lang.reflect.Field;
14 15
15public class StringInfoAccessor { 16public class StringInfoAccessor {
16 17
17 private static Class<?> clazz; 18 private static Class<?> clazz;
18 private static Field stringIndex; 19 private static Field stringIndex;
19 20
20 private Object item; 21 static {
21 22 try {
22 public StringInfoAccessor(Object item) { 23 clazz = Class.forName("javassist.bytecode.StringInfo");
23 this.item = item; 24 stringIndex = clazz.getDeclaredField("string");
24 } 25 stringIndex.setAccessible(true);
25 26 } catch (Exception ex) {
26 public int getStringIndex() { 27 throw new Error(ex);
27 try { 28 }
28 return (Integer) stringIndex.get(this.item); 29 }
29 } catch (Exception ex) { 30
30 throw new Error(ex); 31 private Object item;
31 } 32
32 } 33 public StringInfoAccessor(Object item) {
33 34 this.item = item;
34 public void setStringIndex(int val) { 35 }
35 try { 36
36 stringIndex.set(this.item, val); 37 public static boolean isType(ConstInfoAccessor accessor) {
37 } catch (Exception ex) { 38 return clazz.isAssignableFrom(accessor.getItem().getClass());
38 throw new Error(ex); 39 }
39 } 40
40 } 41 public int getStringIndex() {
41 42 try {
42 public static boolean isType(ConstInfoAccessor accessor) { 43 return (Integer) stringIndex.get(this.item);
43 return clazz.isAssignableFrom(accessor.getItem().getClass()); 44 } catch (Exception ex) {
44 } 45 throw new Error(ex);
45 46 }
46 static { 47 }
47 try { 48
48 clazz = Class.forName("javassist.bytecode.StringInfo"); 49 public void setStringIndex(int val) {
49 stringIndex = clazz.getDeclaredField("string"); 50 try {
50 stringIndex.setAccessible(true); 51 stringIndex.set(this.item, val);
51 } catch (Exception ex) { 52 } catch (Exception ex) {
52 throw new Error(ex); 53 throw new Error(ex);
53 } 54 }
54 } 55 }
55} 56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
index 7a2cb66..cc3b41b 100644
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
@@ -8,21 +8,22 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.bytecode.accessors; 12package cuchaz.enigma.bytecode.accessors;
12 13
13public class Utf8InfoAccessor { 14public class Utf8InfoAccessor {
14 15
15 private static Class<?> clazz; 16 private static Class<?> clazz;
16 17
17 static { 18 static {
18 try { 19 try {
19 clazz = Class.forName("javassist.bytecode.Utf8Info"); 20 clazz = Class.forName("javassist.bytecode.Utf8Info");
20 } catch (Exception ex) { 21 } catch (Exception ex) {
21 throw new Error(ex); 22 throw new Error(ex);
22 } 23 }
23 } 24 }
24 25
25 public static boolean isType(ConstInfoAccessor accessor) { 26 public static boolean isType(ConstInfoAccessor accessor) {
26 return clazz.isAssignableFrom(accessor.getItem().getClass()); 27 return clazz.isAssignableFrom(accessor.getItem().getClass());
27 } 28 }
28} 29}