/******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.bytecode.accessors; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import cuchaz.enigma.bytecode.InfoType; public class ConstInfoAccessor { private static Class m_class; private static Field m_index; private static Method m_getTag; static { try { m_class = Class.forName( "javassist.bytecode.ConstInfo" ); m_index = m_class.getDeclaredField( "index" ); m_index.setAccessible( true ); m_getTag = m_class.getMethod( "getTag" ); m_getTag.setAccessible( true ); } catch( Exception ex ) { throw new Error( ex ); } } private Object m_item; public ConstInfoAccessor( Object item ) { if( item == null ) { throw new IllegalArgumentException( "item cannot be null!" ); } m_item = item; } public ConstInfoAccessor( DataInputStream in ) throws IOException { try { // read the entry String className = in.readUTF(); int oldIndex = in.readInt(); // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back // so we have to read it here in.readByte(); Constructor constructor = Class.forName( className ).getConstructor( DataInputStream.class, int.class ); constructor.setAccessible( true ); m_item = constructor.newInstance( in, oldIndex ); } catch( IOException ex ) { throw ex; } catch( Exception ex ) { throw new Error( ex ); } } public Object getItem( ) { return m_item; } public int getIndex( ) { try { return (Integer)m_index.get( m_item ); } catch( Exception ex ) { throw new Error( ex ); } } public void setIndex( int val ) { try { m_index.set( m_item, val ); } catch( Exception ex ) { throw new Error( ex ); } } public int getTag( ) { try { return (Integer)m_getTag.invoke( m_item ); } catch( Exception ex ) { throw new Error( ex ); } } public ConstInfoAccessor copy( ) { return new ConstInfoAccessor( copyItem() ); } public Object copyItem( ) { // I don't know of a simpler way to copy one of these silly things... try { // serialize the item ByteArrayOutputStream buf = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream( buf ); write( out ); // deserialize the item DataInputStream in = new DataInputStream( new ByteArrayInputStream( buf.toByteArray() ) ); Object item = new ConstInfoAccessor( in ).getItem(); in.close(); return item; } catch( Exception ex ) { throw new Error( ex ); } } public void write( DataOutputStream out ) throws IOException { try { out.writeUTF( m_item.getClass().getName() ); out.writeInt( getIndex() ); Method method = m_item.getClass().getMethod( "write", DataOutputStream.class ); method.setAccessible( true ); method.invoke( m_item, out ); } catch( IOException ex ) { throw ex; } catch( Exception ex ) { throw new Error( ex ); } } @Override public String toString( ) { try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); PrintWriter out = new PrintWriter( buf ); Method print = m_item.getClass().getMethod( "print", PrintWriter.class ); print.setAccessible( true ); print.invoke( m_item, out ); out.close(); return buf.toString().replace( "\n", "" ); } catch( Exception ex ) { throw new Error( ex ); } } public InfoType getType( ) { return InfoType.getByTag( getTag() ); } }