summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/bytecode/ConstPoolEditor.java')
-rw-r--r--src/cuchaz/enigma/bytecode/ConstPoolEditor.java316
1 files changed, 316 insertions, 0 deletions
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}