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