/******************************************************************************* * 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()); } }