summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode
diff options
context:
space:
mode:
authorGravatar jeff2014-07-27 22:33:21 -0400
committerGravatar jeff2014-07-27 22:33:21 -0400
commitd7321b5b0d38c575e54c770f7aa18dacbacab3c8 (patch)
treeef4b3e0f83b1fe89125c2674fec023871e70c0d8 /src/cuchaz/enigma/bytecode
parentmade gui responsive to caret position and show identifier info (diff)
downloadenigma-fork-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.gz
enigma-fork-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.xz
enigma-fork-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.zip
added identifier renaming capability
copied some code over from M3L to handle the heavy bytecode magic. It's ok... M3L will eventually depend on Enigma. Completely restructured the mappings though. This way is better. =)
Diffstat (limited to 'src/cuchaz/enigma/bytecode')
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java180
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeTools.java269
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java171
-rw-r--r--src/cuchaz/enigma/bytecode/ConstPoolEditor.java316
-rw-r--r--src/cuchaz/enigma/bytecode/InfoType.java364
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java199
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java33
14 files changed, 2123 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
new file mode 100644
index 0000000..aadbeb2
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
@@ -0,0 +1,180 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.Bytecode;
17import javassist.bytecode.CodeAttribute;
18import javassist.bytecode.CodeIterator;
19import javassist.bytecode.Opcode;
20
21public class BytecodeIndexIterator implements Iterator<BytecodeIndexIterator.Index>
22{
23 public static class Index
24 {
25 private CodeIterator m_iter;
26 private int m_pos;
27 private boolean m_isWide;
28
29 protected Index( CodeIterator iter, int pos, boolean isWide )
30 {
31 m_iter = iter;
32 m_pos = pos;
33 m_isWide = isWide;
34 }
35
36 public int getIndex( )
37 {
38 if( m_isWide )
39 {
40 return m_iter.s16bitAt( m_pos );
41 }
42 else
43 {
44 return m_iter.byteAt( m_pos );
45 }
46 }
47
48 public void setIndex( int val )
49 throws BadBytecode
50 {
51 if( m_isWide )
52 {
53 m_iter.write16bit( val, m_pos );
54 }
55 else
56 {
57 if( val < 256 )
58 {
59 // we can write the byte
60 m_iter.writeByte( val, m_pos );
61 }
62 else
63 {
64 // we need to upgrade this instruction to LDC_W
65 assert( m_iter.byteAt( m_pos - 1 ) == Opcode.LDC );
66 m_iter.insertGap( m_pos - 1, 1 );
67 m_iter.writeByte( Opcode.LDC_W, m_pos - 1 );
68 m_iter.write16bit( val, m_pos );
69 m_isWide = true;
70
71 // move the iterator to the next opcode
72 m_iter.move( m_pos + 2 );
73 }
74 }
75
76 // sanity check
77 assert( val == getIndex() );
78 }
79
80 public boolean isValid( Bytecode bytecode )
81 {
82 return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize();
83 }
84 }
85
86 private Bytecode m_bytecode;
87 private CodeAttribute m_attribute;
88 private CodeIterator m_iter;
89 private Index m_next;
90
91 public BytecodeIndexIterator( Bytecode bytecode )
92 throws BadBytecode
93 {
94 m_bytecode = bytecode;
95 m_attribute = bytecode.toCodeAttribute();
96 m_iter = m_attribute.iterator();
97
98 m_next = getNext();
99 }
100
101 @Override
102 public boolean hasNext( )
103 {
104 return m_next != null;
105 }
106
107 @Override
108 public Index next( )
109 {
110 Index out = m_next;
111 try
112 {
113 m_next = getNext();
114 }
115 catch( BadBytecode ex )
116 {
117 throw new Error( ex );
118 }
119 return out;
120 }
121
122 @Override
123 public void remove( )
124 {
125 throw new UnsupportedOperationException();
126 }
127
128 private Index getNext( )
129 throws BadBytecode
130 {
131 while( m_iter.hasNext() )
132 {
133 int pos = m_iter.next();
134 int opcode = m_iter.byteAt( pos );
135 switch( opcode )
136 {
137 // for only these opcodes, the next two bytes are a const pool reference
138 case Opcode.ANEWARRAY:
139 case Opcode.CHECKCAST:
140 case Opcode.INSTANCEOF:
141 case Opcode.INVOKEDYNAMIC:
142 case Opcode.INVOKEINTERFACE:
143 case Opcode.INVOKESPECIAL:
144 case Opcode.INVOKESTATIC:
145 case Opcode.INVOKEVIRTUAL:
146 case Opcode.LDC_W:
147 case Opcode.LDC2_W:
148 case Opcode.MULTIANEWARRAY:
149 case Opcode.NEW:
150 case Opcode.PUTFIELD:
151 case Opcode.PUTSTATIC:
152 case Opcode.GETFIELD:
153 case Opcode.GETSTATIC:
154 return new Index( m_iter, pos + 1, true );
155
156 case Opcode.LDC:
157 return new Index( m_iter, pos + 1, false );
158 }
159 }
160
161 return null;
162 }
163
164 public Iterable<Index> indices( )
165 {
166 return new Iterable<Index>( )
167 {
168 @Override
169 public Iterator<Index> iterator( )
170 {
171 return BytecodeIndexIterator.this;
172 }
173 };
174 }
175
176 public void saveChangesToBytecode( )
177 {
178 BytecodeTools.setBytecode( m_bytecode, m_attribute.getCode() );
179 }
180}
diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java
new file mode 100644
index 0000000..664350e
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java
@@ -0,0 +1,269 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.io.ByteArrayInputStream;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.util.Map;
19import java.util.Set;
20
21import javassist.CtBehavior;
22import javassist.bytecode.BadBytecode;
23import javassist.bytecode.Bytecode;
24import javassist.bytecode.CodeAttribute;
25import javassist.bytecode.ConstPool;
26import javassist.bytecode.ExceptionTable;
27
28import com.google.common.collect.Maps;
29import com.google.common.collect.Sets;
30
31import cuchaz.enigma.Util;
32import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index;
33import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
34
35public class BytecodeTools
36{
37 public static byte[] writeBytecode( Bytecode bytecode )
38 throws IOException
39 {
40 ByteArrayOutputStream buf = new ByteArrayOutputStream();
41 DataOutputStream out = new DataOutputStream( buf );
42 try
43 {
44 // write the constant pool
45 new ConstPoolEditor( bytecode.getConstPool() ).writePool( out );
46
47 // write metadata
48 out.writeShort( bytecode.getMaxStack() );
49 out.writeShort( bytecode.getMaxLocals() );
50 out.writeShort( bytecode.getStackDepth() );
51
52 // write the code
53 out.writeShort( bytecode.getSize() );
54 out.write( bytecode.get() );
55
56 // write the exception table
57 int numEntries = bytecode.getExceptionTable().size();
58 out.writeShort( numEntries );
59 for( int i=0; i<numEntries; i++ )
60 {
61 out.writeShort( bytecode.getExceptionTable().startPc( i ) );
62 out.writeShort( bytecode.getExceptionTable().endPc( i ) );
63 out.writeShort( bytecode.getExceptionTable().handlerPc( i ) );
64 out.writeShort( bytecode.getExceptionTable().catchType( i ) );
65 }
66
67 out.close();
68 return buf.toByteArray();
69 }
70 catch( Exception ex )
71 {
72 Util.closeQuietly( out );
73 throw new Error( ex );
74 }
75 }
76
77 public static Bytecode readBytecode( byte[] bytes )
78 throws IOException
79 {
80 ByteArrayInputStream buf = new ByteArrayInputStream( bytes );
81 DataInputStream in = new DataInputStream( buf );
82 try
83 {
84 // read the constant pool entries and update the class
85 ConstPool pool = ConstPoolEditor.readPool( in );
86
87 // read metadata
88 int maxStack = in.readShort();
89 int maxLocals = in.readShort();
90 int stackDepth = in.readShort();
91
92 Bytecode bytecode = new Bytecode( pool, maxStack, maxLocals );
93 bytecode.setStackDepth( stackDepth );
94
95 // read the code
96 int size = in.readShort();
97 byte[] code = new byte[size];
98 in.read( code );
99 setBytecode( bytecode, code );
100
101 // read the exception table
102 int numEntries = in.readShort();
103 for( int i=0; i<numEntries; i++ )
104 {
105 bytecode.getExceptionTable().add( in.readShort(), in.readShort(), in.readShort(), in.readShort() );
106 }
107
108 in.close();
109 return bytecode;
110 }
111 catch( Exception ex )
112 {
113 Util.closeQuietly( in );
114 throw new Error( ex );
115 }
116 }
117
118 public static Bytecode prepareMethodForBytecode( CtBehavior behavior, Bytecode bytecode )
119 throws BadBytecode
120 {
121 // update the destination class const pool
122 bytecode = copyBytecodeToConstPool( behavior.getMethodInfo().getConstPool(), bytecode );
123
124 // update method locals and stack
125 CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute();
126 if( bytecode.getMaxLocals() > attribute.getMaxLocals() )
127 {
128 attribute.setMaxLocals( bytecode.getMaxLocals() );
129 }
130 if( bytecode.getMaxStack() > attribute.getMaxStack() )
131 {
132 attribute.setMaxStack( bytecode.getMaxStack() );
133 }
134
135 return bytecode;
136 }
137
138 public static Bytecode copyBytecodeToConstPool( ConstPool dest, Bytecode bytecode )
139 throws BadBytecode
140 {
141 // get the entries this bytecode needs from the const pool
142 Set<Integer> indices = Sets.newTreeSet();
143 ConstPoolEditor editor = new ConstPoolEditor( bytecode.getConstPool() );
144 BytecodeIndexIterator iterator = new BytecodeIndexIterator( bytecode );
145 for( Index index : iterator.indices() )
146 {
147 assert( index.isValid( bytecode ) );
148 InfoType.gatherIndexTree( indices, editor, index.getIndex() );
149 }
150
151 Map<Integer,Integer> indexMap = Maps.newTreeMap();
152
153 ConstPool src = bytecode.getConstPool();
154 ConstPoolEditor editorSrc = new ConstPoolEditor( src );
155 ConstPoolEditor editorDest = new ConstPoolEditor( dest );
156
157 // copy entries over in order of level so the index mapping is easier
158 for( InfoType type : InfoType.getSortedByLevel() )
159 {
160 for( int index : indices )
161 {
162 ConstInfoAccessor entry = editorSrc.getItem( index );
163
164 // skip entries that aren't this type
165 if( entry.getType() != type )
166 {
167 continue;
168 }
169
170 // make sure the source entry is valid before we copy it
171 assert( type.subIndicesAreValid( entry, editorSrc ) );
172 assert( type.selfIndexIsValid( entry, editorSrc ) );
173
174 // make a copy of the entry so we can modify it safely
175 ConstInfoAccessor entryCopy = editorSrc.getItem( index ).copy();
176 assert( type.subIndicesAreValid( entryCopy, editorSrc ) );
177 assert( type.selfIndexIsValid( entryCopy, editorSrc ) );
178
179 // remap the indices
180 type.remapIndices( indexMap, entryCopy );
181 assert( type.subIndicesAreValid( entryCopy, editorDest ) );
182
183 // put the copy in the destination pool
184 int newIndex = editorDest.addItem( entryCopy.getItem() );
185 entryCopy.setIndex( newIndex );
186 assert( type.selfIndexIsValid( entryCopy, editorDest ) ) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem( entryCopy.getIndex() );
187
188 // make sure the source entry is unchanged
189 assert( type.subIndicesAreValid( entry, editorSrc ) );
190 assert( type.selfIndexIsValid( entry, editorSrc ) );
191
192 // add the index mapping so we can update the bytecode later
193 if( indexMap.containsKey( index ) )
194 {
195 throw new Error( "Entry at index " + index + " already copied!" );
196 }
197 indexMap.put( index, newIndex );
198 }
199 }
200
201 // make a new bytecode
202 Bytecode newBytecode = new Bytecode( dest, bytecode.getMaxStack(), bytecode.getMaxLocals() );
203 bytecode.setStackDepth( bytecode.getStackDepth() );
204 setBytecode( newBytecode, bytecode.get() );
205 setExceptionTable( newBytecode, bytecode.getExceptionTable() );
206
207 // apply the mappings to the bytecode
208 BytecodeIndexIterator iter = new BytecodeIndexIterator( newBytecode );
209 for( Index index : iter.indices() )
210 {
211 int oldIndex = index.getIndex();
212 Integer newIndex = indexMap.get( oldIndex );
213 if( newIndex != null )
214 {
215 // make sure this mapping makes sense
216 InfoType typeSrc = editorSrc.getItem( oldIndex ).getType();
217 InfoType typeDest = editorDest.getItem( newIndex ).getType();
218 assert( typeSrc == typeDest );
219
220 // apply the mapping
221 index.setIndex( newIndex );
222 }
223 }
224 iter.saveChangesToBytecode();
225
226 // make sure all the indices are valid
227 iter = new BytecodeIndexIterator( newBytecode );
228 for( Index index : iter.indices() )
229 {
230 assert( index.isValid( newBytecode ) );
231 }
232
233 return newBytecode;
234 }
235
236 public static void setBytecode( Bytecode dest, byte[] src )
237 {
238 if( src.length > dest.getSize() )
239 {
240 dest.addGap( src.length - dest.getSize() );
241 }
242 assert( dest.getSize() == src.length );
243 for( int i=0; i<src.length; i++ )
244 {
245 dest.write( i, src[i] );
246 }
247 }
248
249 public static void setExceptionTable( Bytecode dest, ExceptionTable src )
250 {
251 // clear the dest exception table
252 int size = dest.getExceptionTable().size();
253 for( int i=size-1; i>=0; i-- )
254 {
255 dest.getExceptionTable().remove( i );
256 }
257
258 // copy the exception table
259 for( int i=0; i<src.size(); i++ )
260 {
261 dest.getExceptionTable().add(
262 src.startPc( i ),
263 src.endPc( i ),
264 src.handlerPc( i ),
265 src.catchType( i )
266 );
267 }
268 }
269}
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java
new file mode 100644
index 0000000..3b5beeb
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -0,0 +1,171 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.util.HashSet;
14import java.util.Set;
15
16import javassist.ClassMap;
17import javassist.CtBehavior;
18import javassist.CtClass;
19import javassist.CtField;
20import javassist.CtMethod;
21import javassist.bytecode.ConstPool;
22import javassist.bytecode.Descriptor;
23import cuchaz.enigma.mapping.ClassEntry;
24import cuchaz.enigma.mapping.FieldEntry;
25import cuchaz.enigma.mapping.MethodEntry;
26import cuchaz.enigma.mapping.Translator;
27
28public class ClassTranslator
29{
30 private Translator m_translator;
31
32 public ClassTranslator( Translator translator )
33 {
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 {
46 switch( constants.getTag( i ) )
47 {
48 case ConstPool.CONST_Fieldref:
49 {
50 // translate the name
51 FieldEntry entry = new FieldEntry(
52 new ClassEntry( Descriptor.toJvmName( constants.getFieldrefClassName( i ) ) ),
53 constants.getFieldrefName( i )
54 );
55 String translatedName = m_translator.translate( entry );
56 if( translatedName == null )
57 {
58 translatedName = entry.getName();
59 }
60
61 // translate the type
62 String type = constants.getFieldrefType( i );
63 String translatedType = m_translator.translateSignature( type );
64
65 if( !entry.getName().equals( translatedName ) || !type.equals( translatedType ) )
66 {
67 editor.changeMemberrefNameAndType( i, translatedName, translatedType );
68 }
69 }
70 break;
71
72 case ConstPool.CONST_Methodref:
73 case ConstPool.CONST_InterfaceMethodref:
74 {
75 // translate the name and type
76 MethodEntry entry = new MethodEntry(
77 new ClassEntry( Descriptor.toJvmName( editor.getMemberrefClassname( i ) ) ),
78 editor.getMemberrefName( i ),
79 editor.getMemberrefType( i )
80 );
81 String translatedName = m_translator.translate( entry );
82 if( translatedName == null )
83 {
84 translatedName = entry.getName();
85 }
86 String translatedSignature = m_translator.translateSignature( entry.getSignature() );
87
88 if( !entry.getName().equals( translatedName ) || !entry.getSignature().equals( translatedSignature ) )
89 {
90 editor.changeMemberrefNameAndType( i, translatedName, translatedSignature );
91 }
92 }
93 break;
94 }
95 }
96
97 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
98
99 // translate all the fields
100 for( CtField field : c.getDeclaredFields() )
101 {
102 // translate the name
103 FieldEntry entry = new FieldEntry( classEntry, field.getName() );
104 String translatedName = m_translator.translate( entry );
105 if( translatedName != null )
106 {
107 field.setName( translatedName );
108 }
109
110 // translate the type
111 String translatedType = m_translator.translateSignature( field.getFieldInfo().getDescriptor() );
112 field.getFieldInfo().setDescriptor( translatedType );
113 }
114
115 // translate all the methods and constructors
116 for( CtBehavior behavior : c.getDeclaredBehaviors() )
117 {
118 // translate the name
119 MethodEntry entry = new MethodEntry( classEntry, behavior.getName(), behavior.getSignature() );
120 String translatedName = m_translator.translate( entry );
121 if( translatedName != null )
122 {
123 if( behavior instanceof CtMethod )
124 {
125 ((CtMethod)behavior).setName( translatedName );
126 }
127 }
128
129 // translate the type
130 String translatedSignature = m_translator.translateSignature( behavior.getMethodInfo().getDescriptor() );
131 behavior.getMethodInfo().setDescriptor( translatedSignature );
132 }
133
134 // translate all the class names referenced in the code
135 // the above code only changed method/field/reference names and types, but not the class names themselves
136 Set<String> classNames = getAllClassNames( c );
137 ClassMap map = new ClassMap();
138 for( String className : classNames )
139 {
140 String translatedName = m_translator.translateClass( className );
141 if( translatedName != null )
142 {
143 map.put( className, translatedName );
144 }
145 }
146 if( !map.isEmpty() )
147 {
148 c.replaceClassName( map );
149 }
150 }
151
152 private Set<String> getAllClassNames( CtClass c )
153 {
154 final Set<String> names = new HashSet<String>();
155 ClassMap map = new ClassMap( )
156 {
157 @Override
158 public Object get( Object obj )
159 {
160 if( obj instanceof String )
161 {
162 names.add( (String)obj );
163 }
164 return null;
165 }
166 private static final long serialVersionUID = -202160293602070641L;
167 };
168 c.replaceClassName( map );
169 return names;
170 }
171}
diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
new file mode 100644
index 0000000..aa6149c
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
@@ -0,0 +1,316 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
41 try
42 {
43 m_getItem = ConstPool.class.getDeclaredMethod( "getItem", int.class );
44 m_getItem.setAccessible( true );
45
46 m_addItem = ConstPool.class.getDeclaredMethod( "addItem", Class.forName( "javassist.bytecode.ConstInfo" ) );
47 m_addItem.setAccessible( true );
48
49 m_addItem0 = ConstPool.class.getDeclaredMethod( "addItem0", Class.forName( "javassist.bytecode.ConstInfo" ) );
50 m_addItem0.setAccessible( true );
51
52 m_items = ConstPool.class.getDeclaredField( "items" );
53 m_items.setAccessible( true );
54
55 m_cache = ConstPool.class.getDeclaredField( "itemsCache" );
56 m_cache.setAccessible( true );
57
58 m_numItems = ConstPool.class.getDeclaredField( "numOfItems" );
59 m_numItems.setAccessible( true );
60
61 m_objects = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "objects" );
62 m_objects.setAccessible( true );
63
64 m_elements = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "elements" );
65 m_elements.setAccessible( true );
66
67 m_methodWritePool = ConstPool.class.getDeclaredMethod( "write", DataOutputStream.class );
68 m_methodWritePool.setAccessible( true );
69
70 m_constructorPool = ConstPool.class.getDeclaredConstructor( DataInputStream.class );
71 m_constructorPool.setAccessible( true );
72 }
73 catch( Exception ex )
74 {
75 throw new Error( ex );
76 }
77 }
78
79 private ConstPool m_pool;
80
81 public ConstPoolEditor( ConstPool pool )
82 {
83 m_pool = pool;
84 }
85
86 public void writePool( DataOutputStream out )
87 {
88 try
89 {
90 m_methodWritePool.invoke( m_pool, out );
91 }
92 catch( Exception ex )
93 {
94 throw new Error( ex );
95 }
96 }
97
98 public static ConstPool readPool( DataInputStream in )
99 {
100 try
101 {
102 return m_constructorPool.newInstance( in );
103 }
104 catch( Exception ex )
105 {
106 throw new Error( ex );
107 }
108 }
109
110 public String getMemberrefClassname( int memberrefIndex )
111 {
112 return Descriptor.toJvmName( m_pool.getClassInfo( m_pool.getMemberClass( memberrefIndex ) ) );
113 }
114
115 public String getMemberrefName( int memberrefIndex )
116 {
117 return m_pool.getUtf8Info( m_pool.getNameAndTypeName( m_pool.getMemberNameAndType( memberrefIndex ) ) );
118 }
119
120 public String getMemberrefType( int memberrefIndex )
121 {
122 return m_pool.getUtf8Info( m_pool.getNameAndTypeDescriptor( m_pool.getMemberNameAndType( memberrefIndex ) ) );
123 }
124
125 public ConstInfoAccessor getItem( int index )
126 {
127 try
128 {
129 Object entry = m_getItem.invoke( m_pool, index );
130 if( entry == null )
131 {
132 return null;
133 }
134 return new ConstInfoAccessor( entry );
135 }
136 catch( Exception ex )
137 {
138 throw new Error( ex );
139 }
140 }
141
142 public int addItem( Object item )
143 {
144 try
145 {
146 return (Integer)m_addItem.invoke( m_pool, item );
147 }
148 catch( Exception ex )
149 {
150 throw new Error( ex );
151 }
152 }
153
154 public int addItemForceNew( Object item )
155 {
156 try
157 {
158 return (Integer)m_addItem0.invoke( m_pool, item );
159 }
160 catch( Exception ex )
161 {
162 throw new Error( ex );
163 }
164 }
165 @SuppressWarnings( "rawtypes" )
166 public void removeLastItem( )
167 {
168 try
169 {
170 // remove the item from the cache
171 HashMap cache = getCache();
172 if( cache != null )
173 {
174 Object item = getItem( m_pool.getSize() - 1 );
175 cache.remove( item );
176 }
177
178 // remove the actual item
179 // based off of LongVector.addElement()
180 Object items = m_items.get( m_pool );
181 Object[][] objects = (Object[][])m_objects.get( items );
182 int numElements = (Integer)m_elements.get( items ) - 1;
183 int nth = numElements >> 7;
184 int offset = numElements & (128 - 1);
185 objects[nth][offset] = null;
186
187 // decrement the number of items
188 m_elements.set( items, numElements );
189 m_numItems.set( m_pool, (Integer)m_numItems.get( m_pool ) - 1 );
190 }
191 catch( Exception ex )
192 {
193 throw new Error( ex );
194 }
195 }
196
197 @SuppressWarnings( "rawtypes" )
198 /* TEMP */public HashMap getCache( )
199 {
200 try
201 {
202 return (HashMap)m_cache.get( m_pool );
203 }
204 catch( Exception ex )
205 {
206 throw new Error( ex );
207 }
208 }
209
210 @SuppressWarnings( { "rawtypes", "unchecked" } )
211 public void changeMemberrefNameAndType( int memberrefIndex, String newName, String newType )
212 {
213 // NOTE: when changing values, we always need to copy-on-write
214 try
215 {
216 // get the memberref item
217 Object item = getItem( memberrefIndex ).getItem();
218
219 // update the cache
220 HashMap cache = getCache();
221 if( cache != null )
222 {
223 cache.remove( item );
224 }
225
226 new MemberRefInfoAccessor( item ).setNameAndTypeIndex( m_pool.addNameAndTypeInfo( newName, newType ) );
227
228 // update the cache
229 if( cache != null )
230 {
231 cache.put( item, item );
232 }
233 }
234 catch( Exception ex )
235 {
236 throw new Error( ex );
237 }
238
239 // make sure the change worked
240 assert( newName.equals( getMemberrefName( memberrefIndex ) ) );
241 assert( newType.equals( getMemberrefType( memberrefIndex ) ) );
242 }
243
244 @SuppressWarnings( { "rawtypes", "unchecked" } )
245 public void changeClassName( int classNameIndex, String newName )
246 {
247 // NOTE: when changing values, we always need to copy-on-write
248 try
249 {
250 // get the class item
251 Object item = getItem( classNameIndex ).getItem();
252
253 // update the cache
254 HashMap cache = getCache();
255 if( cache != null )
256 {
257 cache.remove( item );
258 }
259
260 // add the new name and repoint the name-and-type to it
261 new ClassInfoAccessor( item ).setNameIndex( m_pool.addUtf8Info( newName ) );
262
263 // update the cache
264 if( cache != null )
265 {
266 cache.put( item, item );
267 }
268 }
269 catch( Exception ex )
270 {
271 throw new Error( ex );
272 }
273 }
274
275 public static ConstPool newConstPool( )
276 {
277 // const pool expects the name of a class to initialize itself
278 // but we want an empty pool
279 // so give it a bogus name, and then clear the entries afterwards
280 ConstPool pool = new ConstPool( "a" );
281
282 ConstPoolEditor editor = new ConstPoolEditor( pool );
283 int size = pool.getSize();
284 for( int i=0; i<size-1; i++ )
285 {
286 editor.removeLastItem();
287 }
288
289 // make sure the pool is actually empty
290 // although, in this case "empty" means one thing in it
291 // the JVM spec says index 0 should be reserved
292 assert( pool.getSize() == 1 );
293 assert( editor.getItem( 0 ) == null );
294 assert( editor.getItem( 1 ) == null );
295 assert( editor.getItem( 2 ) == null );
296 assert( editor.getItem( 3 ) == null );
297
298 // also, clear the cache
299 editor.getCache().clear();
300
301 return pool;
302 }
303
304 public String dump( )
305 {
306 StringBuilder buf = new StringBuilder();
307 for( int i=1; i<m_pool.getSize(); i++ )
308 {
309 buf.append( String.format( "%4d", i ) );
310 buf.append( " " );
311 buf.append( getItem( i ).toString() );
312 buf.append( "\n" );
313 }
314 return buf.toString();
315 }
316}
diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java
new file mode 100644
index 0000000..fe03006
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/InfoType.java
@@ -0,0 +1,364 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
41 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
42 gatherIndexTree( indices, editor, accessor.getNameIndex() );
43 }
44
45 @Override
46 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
47 {
48 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
49 accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) );
50 }
51
52 @Override
53 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
54 {
55 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
56 ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() );
57 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
58 }
59 },
60 StringInfo( 8, 1 )
61 {
62 @Override
63 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
64 {
65 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
66 gatherIndexTree( indices, editor, accessor.getStringIndex() );
67 }
68
69 @Override
70 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
71 {
72 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
73 accessor.setStringIndex( remapIndex( map, accessor.getStringIndex() ) );
74 }
75
76 @Override
77 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
78 {
79 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
80 ConstInfoAccessor stringEntry = pool.getItem( accessor.getStringIndex() );
81 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
82 }
83 },
84 FieldRefInfo( 9, 2 )
85 {
86 @Override
87 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
88 {
89 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
90 gatherIndexTree( indices, editor, accessor.getClassIndex() );
91 gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() );
92 }
93
94 @Override
95 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
96 {
97 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
98 accessor.setClassIndex( remapIndex( map, accessor.getClassIndex() ) );
99 accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) );
100 }
101
102 @Override
103 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
104 {
105 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
106 ConstInfoAccessor classEntry = pool.getItem( accessor.getClassIndex() );
107 ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() );
108 return classEntry != null && classEntry.getTag() == ClassInfo.getTag()
109 && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
110 }
111 },
112 MethodRefInfo( 10, 2 ) // same as FieldRefInfo
113 {
114 @Override
115 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
116 {
117 FieldRefInfo.gatherIndexTree( indices, editor, entry );
118 }
119
120 @Override
121 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
122 {
123 FieldRefInfo.remapIndices( map, entry );
124 }
125
126 @Override
127 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
128 {
129 return FieldRefInfo.subIndicesAreValid( entry, pool );
130 }
131 },
132 InterfaceMethodRefInfo( 11, 2 ) // same as FieldRefInfo
133 {
134 @Override
135 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
136 {
137 FieldRefInfo.gatherIndexTree( indices, editor, entry );
138 }
139
140 @Override
141 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
142 {
143 FieldRefInfo.remapIndices( map, entry );
144 }
145
146 @Override
147 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
148 {
149 return FieldRefInfo.subIndicesAreValid( entry, pool );
150 }
151 },
152 NameAndTypeInfo( 12, 1 )
153 {
154 @Override
155 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
156 {
157 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
158 gatherIndexTree( indices, editor, accessor.getNameIndex() );
159 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
160 }
161
162 @Override
163 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
164 {
165 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
166 accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) );
167 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
168 }
169
170 @Override
171 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
172 {
173 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
174 ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() );
175 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
176 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag()
177 && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
178 }
179 },
180 MethodHandleInfo( 15, 3 )
181 {
182 @Override
183 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
184 {
185 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
186 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
187 gatherIndexTree( indices, editor, accessor.getMethodRefIndex() );
188 }
189
190 @Override
191 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
192 {
193 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
194 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
195 accessor.setMethodRefIndex( remapIndex( map, accessor.getMethodRefIndex() ) );
196 }
197
198 @Override
199 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
200 {
201 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
202 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
203 ConstInfoAccessor methodRefEntry = pool.getItem( accessor.getMethodRefIndex() );
204 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag()
205 && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
206 }
207 },
208 MethodTypeInfo( 16, 1 )
209 {
210 @Override
211 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
212 {
213 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
214 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
215 }
216
217 @Override
218 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
219 {
220 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
221 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
222 }
223
224 @Override
225 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
226 {
227 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
228 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
229 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
230 }
231 },
232 InvokeDynamicInfo( 18, 2 )
233 {
234 @Override
235 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
236 {
237 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
238 gatherIndexTree( indices, editor, accessor.getBootstrapIndex() );
239 gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() );
240 }
241
242 @Override
243 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
244 {
245 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
246 accessor.setBootstrapIndex( remapIndex( map, accessor.getBootstrapIndex() ) );
247 accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) );
248 }
249
250 @Override
251 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
252 {
253 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
254 ConstInfoAccessor bootstrapEntry = pool.getItem( accessor.getBootstrapIndex() );
255 ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() );
256 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag()
257 && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
258 }
259 };
260
261 private static Map<Integer,InfoType> m_types;
262
263 static
264 {
265 m_types = Maps.newTreeMap();
266 for( InfoType type : values() )
267 {
268 m_types.put( type.getTag(), type );
269 }
270 }
271
272 private int m_tag;
273 private int m_level;
274
275 private InfoType( int tag, int level )
276 {
277 m_tag = tag;
278 m_level = level;
279 }
280
281 public int getTag( )
282 {
283 return m_tag;
284 }
285
286 public int getLevel( )
287 {
288 return m_level;
289 }
290
291 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
292 {
293 // by default, do nothing
294 }
295
296 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
297 {
298 // by default, do nothing
299 }
300
301 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
302 {
303 // by default, everything is good
304 return true;
305 }
306
307 public boolean selfIndexIsValid( ConstInfoAccessor entry, ConstPoolEditor pool )
308 {
309 ConstInfoAccessor entryCheck = pool.getItem( entry.getIndex() );
310 if( entryCheck == null )
311 {
312 return false;
313 }
314 return entryCheck.getItem().equals( entry.getItem() );
315 }
316
317 public static InfoType getByTag( int tag )
318 {
319 return m_types.get( tag );
320 }
321
322 public static List<InfoType> getByLevel( int level )
323 {
324 List<InfoType> types = Lists.newArrayList();
325 for( InfoType type : values() )
326 {
327 if( type.getLevel() == level )
328 {
329 types.add( type );
330 }
331 }
332 return types;
333 }
334
335 public static List<InfoType> getSortedByLevel( )
336 {
337 List<InfoType> types = Lists.newArrayList();
338 types.addAll( getByLevel( 0 ) );
339 types.addAll( getByLevel( 1 ) );
340 types.addAll( getByLevel( 2 ) );
341 types.addAll( getByLevel( 3 ) );
342 return types;
343 }
344
345 public static void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, int index )
346 {
347 // add own index
348 indices.add( index );
349
350 // recurse
351 ConstInfoAccessor entry = editor.getItem( index );
352 entry.getType().gatherIndexTree( indices, editor, entry );
353 }
354
355 private static int remapIndex( Map<Integer,Integer> map, int index )
356 {
357 Integer newIndex = map.get( index );
358 if( newIndex == null )
359 {
360 newIndex = index;
361 }
362 return newIndex;
363 }
364}
diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
new file mode 100644
index 0000000..41e1d04
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
@@ -0,0 +1,69 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.ClassInfo" );
25 m_nameIndex = m_class.getDeclaredField( "name" );
26 m_nameIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public ClassInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getNameIndex( )
47 {
48 try
49 {
50 return (Integer)m_nameIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setNameIndex( int val )
59 {
60 try
61 {
62 m_nameIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
new file mode 100644
index 0000000..3c3d3fa
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
@@ -0,0 +1,199 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
33 try
34 {
35 m_class = Class.forName( "javassist.bytecode.ConstInfo" );
36 m_index = m_class.getDeclaredField( "index" );
37 m_index.setAccessible( true );
38 m_getTag = m_class.getMethod( "getTag" );
39 m_getTag.setAccessible( true );
40 }
41 catch( Exception ex )
42 {
43 throw new Error( ex );
44 }
45 }
46
47 private Object m_item;
48
49 public ConstInfoAccessor( Object item )
50 {
51 if( item == null )
52 {
53 throw new IllegalArgumentException( "item cannot be null!" );
54 }
55 m_item = item;
56 }
57
58 public ConstInfoAccessor( DataInputStream in )
59 throws IOException
60 {
61 try
62 {
63 // read the entry
64 String className = in.readUTF();
65 int oldIndex = in.readInt();
66
67 // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back
68 // so we have to read it here
69 in.readByte();
70
71 Constructor<?> constructor = Class.forName( className ).getConstructor( DataInputStream.class, int.class );
72 constructor.setAccessible( true );
73 m_item = constructor.newInstance( in, oldIndex );
74 }
75 catch( IOException ex )
76 {
77 throw ex;
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public Object getItem( )
86 {
87 return m_item;
88 }
89
90 public int getIndex( )
91 {
92 try
93 {
94 return (Integer)m_index.get( m_item );
95 }
96 catch( Exception ex )
97 {
98 throw new Error( ex );
99 }
100 }
101
102 public void setIndex( int val )
103 {
104 try
105 {
106 m_index.set( m_item, val );
107 }
108 catch( Exception ex )
109 {
110 throw new Error( ex );
111 }
112 }
113
114 public int getTag( )
115 {
116 try
117 {
118 return (Integer)m_getTag.invoke( m_item );
119 }
120 catch( Exception ex )
121 {
122 throw new Error( ex );
123 }
124 }
125
126 public ConstInfoAccessor copy( )
127 {
128 return new ConstInfoAccessor( copyItem() );
129 }
130
131 public Object copyItem( )
132 {
133 // I don't know of a simpler way to copy one of these silly things...
134 try
135 {
136 // serialize the item
137 ByteArrayOutputStream buf = new ByteArrayOutputStream();
138 DataOutputStream out = new DataOutputStream( buf );
139 write( out );
140
141 // deserialize the item
142 DataInputStream in = new DataInputStream( new ByteArrayInputStream( buf.toByteArray() ) );
143 Object item = new ConstInfoAccessor( in ).getItem();
144 in.close();
145
146 return item;
147 }
148 catch( Exception ex )
149 {
150 throw new Error( ex );
151 }
152 }
153
154 public void write( DataOutputStream out )
155 throws IOException
156 {
157 try
158 {
159 out.writeUTF( m_item.getClass().getName() );
160 out.writeInt( getIndex() );
161
162 Method method = m_item.getClass().getMethod( "write", DataOutputStream.class );
163 method.setAccessible( true );
164 method.invoke( m_item, out );
165 }
166 catch( IOException ex )
167 {
168 throw ex;
169 }
170 catch( Exception ex )
171 {
172 throw new Error( ex );
173 }
174 }
175
176 @Override
177 public String toString( )
178 {
179 try
180 {
181 ByteArrayOutputStream buf = new ByteArrayOutputStream();
182 PrintWriter out = new PrintWriter( buf );
183 Method print = m_item.getClass().getMethod( "print", PrintWriter.class );
184 print.setAccessible( true );
185 print.invoke( m_item, out );
186 out.close();
187 return buf.toString().replace( "\n", "" );
188 }
189 catch( Exception ex )
190 {
191 throw new Error( ex );
192 }
193 }
194
195 public InfoType getType( )
196 {
197 return InfoType.getByTag( getTag() );
198 }
199}
diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
new file mode 100644
index 0000000..169306a
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.InvokeDynamicInfo" );
26 m_bootstrapIndex = m_class.getDeclaredField( "bootstrap" );
27 m_bootstrapIndex.setAccessible( true );
28 m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndType" );
29 m_nameAndTypeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public InvokeDynamicInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getBootstrapIndex( )
50 {
51 try
52 {
53 return (Integer)m_bootstrapIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setBootstrapIndex( int val )
62 {
63 try
64 {
65 m_bootstrapIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getNameAndTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_nameAndTypeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setNameAndTypeIndex( int val )
86 {
87 try
88 {
89 m_nameAndTypeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
new file mode 100644
index 0000000..2ee3aff
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.MemberrefInfo" );
26 m_classIndex = m_class.getDeclaredField( "classIndex" );
27 m_classIndex.setAccessible( true );
28 m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndTypeIndex" );
29 m_nameAndTypeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public MemberRefInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getClassIndex( )
50 {
51 try
52 {
53 return (Integer)m_classIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setClassIndex( int val )
62 {
63 try
64 {
65 m_classIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getNameAndTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_nameAndTypeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setNameAndTypeIndex( int val )
86 {
87 try
88 {
89 m_nameAndTypeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
new file mode 100644
index 0000000..27b7aee
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.MethodHandleInfo" );
26 m_kindIndex = m_class.getDeclaredField( "refKind" );
27 m_kindIndex.setAccessible( true );
28 m_indexIndex = m_class.getDeclaredField( "refIndex" );
29 m_indexIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public MethodHandleInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getTypeIndex( )
50 {
51 try
52 {
53 return (Integer)m_kindIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setTypeIndex( int val )
62 {
63 try
64 {
65 m_kindIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getMethodRefIndex( )
74 {
75 try
76 {
77 return (Integer)m_indexIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setMethodRefIndex( int val )
86 {
87 try
88 {
89 m_indexIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
new file mode 100644
index 0000000..4cba6a2
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
@@ -0,0 +1,69 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.MethodTypeInfo" );
25 m_descriptorIndex = m_class.getDeclaredField( "descriptor" );
26 m_descriptorIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public MethodTypeInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getTypeIndex( )
47 {
48 try
49 {
50 return (Integer)m_descriptorIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setTypeIndex( int val )
59 {
60 try
61 {
62 m_descriptorIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
new file mode 100644
index 0000000..03b4de3
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.NameAndTypeInfo" );
26 m_nameIndex = m_class.getDeclaredField( "memberName" );
27 m_nameIndex.setAccessible( true );
28 m_typeIndex = m_class.getDeclaredField( "typeDescriptor" );
29 m_typeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public NameAndTypeInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getNameIndex( )
50 {
51 try
52 {
53 return (Integer)m_nameIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setNameIndex( int val )
62 {
63 try
64 {
65 m_nameIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_typeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setTypeIndex( int val )
86 {
87 try
88 {
89 m_typeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
new file mode 100644
index 0000000..5cdfce4
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
@@ -0,0 +1,69 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.StringInfo" );
25 m_stringIndex = m_class.getDeclaredField( "string" );
26 m_stringIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public StringInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getStringIndex( )
47 {
48 try
49 {
50 return (Integer)m_stringIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setStringIndex( int val )
59 {
60 try
61 {
62 m_stringIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
new file mode 100644
index 0000000..1cadd83
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
@@ -0,0 +1,33 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 {
19 try
20 {
21 m_class = Class.forName( "javassist.bytecode.Utf8Info" );
22 }
23 catch( Exception ex )
24 {
25 throw new Error( ex );
26 }
27 }
28
29 public static boolean isType( ConstInfoAccessor accessor )
30 {
31 return m_class.isAssignableFrom( accessor.getItem().getClass() );
32 }
33}