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/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}