summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
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/ConstPoolEditor.java
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/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}