読み取りはできたけど、途中で飽きた別にこんなことやる必要なかったので、
名前の対応付けとかその辺はやってないです。あと、printデバッグも消してないです。
バイナリ操作をするためにbyte列を扱うためのユーティリティを書いた(これがメインだったりする)けど、
割と使いにくかったので、やる気になったら今後改善したい。
見所はClassFileの実装のゴリオシ感
参考(まんま。)
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
ClassFile.java
package my.loader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * とりあえずClassFileを読み出すサンプル * * @author ryozi * */ public class ClassFile { private File classFile = null; private MetaData metadata = new MetaData(); /** * ファイルを与えると読み込みます。 * * @param filepath * @throws IOException */ public ClassFile(String filepath) throws IOException { classFile = new File(filepath); load(); } /** * 勝手に呼ばれます * * @throws IOException */ public void load() throws IOException { InputStream is = new FileInputStream(classFile); MetaData meta = new MetaData(); metadata = meta; // マジックナンバー読み取り & 判定 is.read(meta.magic); if (meta.getMagic() != 0xCAFEBABE) { throw new RuntimeException("Javaファイルではありません"); } System.out.println("MAGIC_NUMBER: " + ByteUtil.toHexString(metadata.magic, false)); // マイナーバージョン読み取り is.read(meta.minor_version); is.read(meta.major_version); System.out.println("minor_version: " + ByteUtil.toHexString(metadata.minor_version, false)); System.out.println("major_version: " + ByteUtil.toHexString(metadata.major_version, false)); // constant_pool読み取り is.read(meta.constant_pool_count); meta.constant_pool = new cp_info[meta.getConstant_pool_count() - 1]; System.out.println("constant_pool_count: " + ByteUtil.toHexString(metadata.constant_pool_count, false)); byte[] constant_pool_tag = new byte[1]; for (int i = 0; i < meta.getConstant_pool_count() - 1; ++i) { is.read(constant_pool_tag); meta.constant_pool[i] = newInstance(constant_pool_tag[0]); meta.constant_pool[i].load(is); } // access_flags/this_class/super_class読み取り is.read(meta.access_flags); is.read(meta.this_class); is.read(meta.super_class); // interface読み取り is.read(meta.interfaces_count); meta.interfaces = new byte[ByteUtil.toShort(meta.interfaces_count, true)][2]; for (byte[] a : meta.interfaces) { is.read(a); } // field読み取り is.read(meta.fields_count); meta.fields = new field_info[ByteUtil.toShort(meta.fields_count, true)]; System.out.println("fields_count: " + Integer.toHexString(meta.fields.length)); for (int i = 0; i < meta.fields.length; ++i) { meta.fields[i] = new field_info(); is.read(meta.fields[i].access_flags); is.read(meta.fields[i].name_index); is.read(meta.fields[i].descriptor_index); is.read(meta.fields[i].attributes_count); meta.fields[i].attributes = new attribute_info[ByteUtil.toShort(meta.fields[i].attributes_count, true)]; load_attribute(is, meta.fields[i].attributes); } // method読み取り is.read(meta.methods_count); meta.methods = new method_info[ByteUtil.toShort(meta.methods_count, true)]; for (int i = 0; i < meta.methods.length; ++i) { meta.methods[i] = new method_info(); is.read(meta.methods[i].access_flags); is.read(meta.methods[i].name_index); is.read(meta.methods[i].descriptor_index); is.read(meta.methods[i].attributes_count); meta.methods[i].attributes = new attribute_info[ByteUtil.toShort(meta.methods[i].attributes_count, true)]; load_attribute(is, meta.methods[i].attributes); } is.read(meta.attributes_count); meta.attributes = new attribute_info[ByteUtil.toShort(meta.attributes_count, true)]; load_attribute(is, meta.attributes); long f = classFile.length(); long s = 0; while (is.read() != -1) { s += 1; } ; System.out.println("file size: " + f + " byte"); System.out.println("skip: " + s + " byte"); System.out.println("readed: " + (f - s) + " byte"); } private void load_attribute(InputStream is, attribute_info[] attr) throws IOException { for (int i = 0; i < attr.length; ++i) { attr[i] = new attribute_info(); is.read(attr[i].attribute_name_index); is.read(attr[i].attribute_length); attr[i].info = new byte[ByteUtil.toInt(attr[i].attribute_length, true)][1]; for (byte[] c : attr[i].info) { is.read(c); } } } private cp_info newInstance(byte tag) { switch (tag) { case 1: return new CONSTANT_Utf8_info(); case 3: return new CONSTANT_Integer_info(); case 4: return new CONSTANT_Float_info(); case 5: return new CONSTANT_Long_info(); case 6: return new CONSTANT_Double_info(); case 7: return new CONSTANT_Class_info(); case 8: return new CONSTANT_String_info(); case 9: return new CONSTANT_Fieldref_info(); case 10: return new CONSTANT_Methodref_info(); case 11: return new CONSTANT_InterfaceMethodref_info(); case 12: return new CONSTANT_NameAndType_info(); default: throw new RuntimeException("サポートしないタグ:" + tag); } } class MetaData { byte[] magic = new byte[4]; byte[] minor_version = new byte[2]; byte[] major_version = new byte[2]; byte[] constant_pool_count = new byte[2]; cp_info constant_pool[] = null; // length = constant_pool_count - 1 byte[] access_flags = new byte[2]; byte[] this_class = new byte[2]; byte[] super_class = new byte[2]; byte[] interfaces_count = new byte[2]; byte[] interfaces[] = null; // value u2, length = interfaces_count - // 1 byte[] fields_count = new byte[2]; field_info fields[] = null; byte[] methods_count = new byte[2]; method_info methods[] = null; byte[] attributes_count = new byte[2]; attribute_info attributes[] = null; public int getMagic() { return ByteUtil.toInt(magic, true); } public short getConstant_pool_count() { return ByteUtil.toShort(constant_pool_count, true); } } class field_info { byte[] access_flags = new byte[2]; byte[] name_index = new byte[2]; byte[] descriptor_index = new byte[2]; byte[] attributes_count = new byte[2]; attribute_info attributes[] = null; // length = attributes_count; } class method_info { byte[] access_flags = new byte[2]; byte[] name_index = new byte[2]; byte[] descriptor_index = new byte[2]; byte[] attributes_count = new byte[2]; attribute_info attributes[] = null; // length = attributes_count; } class attribute_info { byte[] attribute_name_index = new byte[2]; byte[] attribute_length = new byte[4]; byte[] info[] = null; // attribute_length }; abstract class cp_info { protected abstract void load(InputStream is) throws IOException; } class CONSTANT_Integer_info extends cp_info { byte[] tag = new byte[] { 3 }; byte[] bytes = new byte[4]; @Override public void load(InputStream is) throws IOException { is.read(bytes); } } // int s = ((bits >> 31) == 0) ? 1 : -1; // int e = ((bits >> 23) & 0xff); // int m = (e == 0) ? // (bits & 0x7fffff) << 1 : // (bits & 0x7fffff) | 0x800000; // // Then the float value equals the result of the mathematical expression // s·m·2(e-150). class CONSTANT_Float_info extends cp_info { byte[] tag = new byte[] { 4 }; byte[] bytes = new byte[4]; @Override public void load(InputStream is) throws IOException { is.read(bytes); } } class CONSTANT_Long_info extends cp_info { byte[] tag = new byte[] { 5 }; byte[] high_bytes = new byte[4]; byte[] low_bytes = new byte[4]; @Override public void load(InputStream is) throws IOException { is.read(high_bytes); is.read(low_bytes); } } class CONSTANT_Double_info extends cp_info { byte[] tag = new byte[] { 6 }; byte[] high_bytes = new byte[4]; byte[] low_bytes = new byte[4]; @Override public void load(InputStream is) throws IOException { is.read(high_bytes); is.read(low_bytes); } } class CONSTANT_Class_info extends cp_info { byte[] tag = new byte[] { 7 }; byte[] name_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(name_index); } } class CONSTANT_String_info extends cp_info { byte[] tag = new byte[] { 8 }; byte[] string_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(string_index); } } class CONSTANT_Fieldref_info extends cp_info { byte[] tag = new byte[] { 9 }; byte[] class_index = new byte[2]; byte[] name_and_type_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(class_index); is.read(name_and_type_index); } } class CONSTANT_Methodref_info extends cp_info { byte[] tag = new byte[] { 10 }; byte[] class_index = new byte[2]; byte[] name_and_type_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(class_index); is.read(name_and_type_index); } } class CONSTANT_InterfaceMethodref_info extends cp_info { byte[] tag = new byte[] { 11 }; byte[] class_index = new byte[2]; byte[] name_and_type_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(class_index); is.read(name_and_type_index); } } class CONSTANT_NameAndType_info extends cp_info { byte[] tag = new byte[] { 12 }; byte[] name_index = new byte[2]; byte[] descriptor_index = new byte[2]; @Override public void load(InputStream is) throws IOException { is.read(name_index); is.read(descriptor_index); } } class CONSTANT_Utf8_info extends cp_info { byte[] tag = new byte[] { 1 }; byte[] length = new byte[2]; byte[] bytes = null; // length @Override public void load(InputStream is) throws IOException { is.read(length); bytes = new byte[ByteUtil.toShort(length, true)]; is.read(bytes); System.out.println(new String(bytes)); } } }
ByteUtil.java
package my.loader; import java.util.Arrays; /** * byte列を扱うためのなにか。 * * @author ryozi * */ public class ByteUtil { private static final int[][] ENDIAN_SHORT = new int[][] { { 0, 1 }, { 1, 0 } }; private static final int[][] ENDIAN_INT = new int[][] { { 0, 1, 2, 3 }, { 3, 2, 1, 0 } }; private static final int[][] ENDIAN_LONG = new int[][] { { 0, 1, 2, 3, 4, 5, 6, 7 }, { 7, 6, 5, 4, 3, 2, 1, 0 } }; /** * 符号なしbyteの値を取得します。 * 戻り値がlongであることに注意してください。 * * @param x * @return */ public static long toUnsignedByte(long x) { return x & 0x00000000000000FFL; } /** * 符号なしshortの値を取得します。 * 戻り値がlongであることに注意してください。 * * @param x * @return */ public static long toUnsignedShort(long x) { return x & 0x000000000000FFFFL; } /** * 符号なしintの値を取得します。 * 戻り値がlongであることに注意してください。 * * @param x * @return */ public static long toUnsignedInt(long x) { return x & 0x00000000FFFFFFFFL; } /** * byte列中の任意の場所から任意のサイズのbyte列をコピーして取得します。 * Arrays#copyOfRange の糖衣構文です。 * * @param bytes * @param offset * @param length * @return */ public static byte[] copyOf(byte[] bytes, int offset, int length) { return Arrays.copyOfRange(bytes, offset, offset + length); } /** * 与えられた値をByte列に変換します。 * longを引数に取りますが、考慮されるのは下位8bitのみです。 * * @param ls * @return */ public static byte[] toByteArray(long... ls) { byte[] bs = new byte[ls.length]; for (int i = 0; i < ls.length; ++i) { bs[i] = (byte) (toUnsignedByte(ls[i])); } return bs; } /** * 2byteのbyte列からshortへ変換します * * @param byte2 * @param isBigEndian * @return */ public static short toShort(byte[] byte2, boolean isBigEndian) { if (byte2.length != 2) { throw new IllegalArgumentException("2byte only"); } final int[] endian = ENDIAN_SHORT[isBigEndian ? 0 : 1]; return (short) ((toUnsignedByte(byte2[endian[0]]) << 8 | toUnsignedByte(byte2[endian[1]]))); } /** * 4byteのbyte列からintへ変換します * * @param byte4 * @param isBigEndian * @return */ public static int toInt(byte[] byte4, boolean isBigEndian) { if (byte4.length != 4) { throw new IllegalArgumentException("4byte only"); } final int[] endian = ENDIAN_INT[isBigEndian ? 0 : 1]; return (int) (toUnsignedByte(byte4[endian[0]]) << 24 | toUnsignedByte(byte4[endian[1]]) << 16 | toUnsignedByte(byte4[endian[2]]) << 8 | toUnsignedByte(byte4[endian[3]]) << 0); } /** * 4byteのbyte列からfloatへ変換します * * @param byte4 * @param isBigEndian * @return */ public static float toFloat(byte[] byte4, boolean isBigEndian) { return toFloat(toInt(byte4, isBigEndian)); } /** * intのbit配列を考慮して、floatへ変換します。 * 無限や、扱えない値によるエラーは考慮されません。 * * int s = ((bits >> 31) == 0) ? 1 : -1; * int e = ((bits >> 23) & 0xff); * int m = (e == 0) ? * (bits & 0x7fffff) << 1 : * (bits & 0x7fffff) | 0x800000; * Then the float value equals the result of the mathematical expression * s·m·2^(e-150). * * @param byte4 * @param isBigEndian * @return */ public static float toFloat(int bits) { int s = (bits >> 31) == 0 ? 1 : -1; int e = (bits >> 23) & 0xFF; int m = (e == 0) ? ((bits & 0x7FFFFF) << 1) : ((bits & 0x7FFFFF) | 0x800000); return (float) (s * m * Math.pow(2, e - 150)); } /** * 8byteのbyte列をlongへ変換します。 * * @param byte8 * @param isBigEndian * @return */ public static long toLong(byte[] byte8, boolean isBigEndian) { if (byte8.length != 8) { throw new IllegalArgumentException("8byte only"); } final int[] endian = ENDIAN_LONG[isBigEndian ? 0 : 1]; return toUnsignedByte(byte8[endian[0]]) << 56 | toUnsignedByte(byte8[endian[1]]) << 48 | toUnsignedByte(byte8[endian[2]]) << 40 | toUnsignedByte(byte8[endian[3]]) << 32 | toUnsignedByte(byte8[endian[4]]) << 24 | toUnsignedByte(byte8[endian[5]]) << 16 | toUnsignedByte(byte8[endian[6]]) << 8 | toUnsignedByte(byte8[endian[7]]); } /** * intを4byteに切り出し、byte列に変換します。 * * @param x * @param isBigEndian * @return */ public static byte[] toByteArray(int x, boolean isBigEndian) { byte[] bytes = new byte[4]; final int[] endian = ENDIAN_INT[isBigEndian ? 0 : 1]; bytes[endian[0]] = (byte) (x >> 24); bytes[endian[1]] = (byte) ((x >> 16) & 0xFF); bytes[endian[2]] = (byte) ((x >> 8) & 0xFF); bytes[endian[3]] = (byte) ((x >> 0) & 0xFF); return bytes; } /** * byte列を16進数変換します。0xを勝手につけます。 * * @param bs * @param isBigEndian * @return */ public static String toHexString(byte[] bs, boolean isBigEndian) { int init = 0; int loop = bs.length; int add = 1; if (isBigEndian) { init = loop - 1; loop = -1; add = -1; } String ret = "0x"; for (int i = init; i != loop; i += add) { ret += String.format("%02X", toUnsignedByte(bs[i])); } return ret; } /** * バイト列からビットを読み取り、0/1の文字になおして書き出します。 * エンディアンは考慮しません。(めんどくさくなったから) * * @param bs * @return */ public static String toBitString(byte[] bs) { String ret = ""; for (int i = 0; i < bs.length; i++) { ret += (bs[i] >> 7) & 1; ret += (bs[i] >> 6) & 1; ret += (bs[i] >> 5) & 1; ret += (bs[i] >> 4) & 1; ret += (bs[i] >> 3) & 1; ret += (bs[i] >> 2) & 1; ret += (bs[i] >> 1) & 1; ret += (bs[i] >> 0) & 1; } return ret; } }
ByteUtilTest.java
テストの書き方とかわかりませんですし
package my.loader.test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import my.loader.ByteUtil; import org.junit.Test; public class ByteUtilTest { @Test public void copyOfTest() { assertArrayEquals(ByteUtil.toByteArray(0x00,0x20,0x40,0x80,0xFF), ByteUtil.copyOf(ByteUtil.toByteArray(0x00,0x20,0x40,0x80,0xFF), 0, 5)); assertArrayEquals(ByteUtil.toByteArray(0x00,0x20,0x40,0x80), ByteUtil.copyOf(ByteUtil.toByteArray(0x00,0x20,0x40,0x80,0xFF), 0, 4)); assertArrayEquals(ByteUtil.toByteArray(0x20,0x40,0x80), ByteUtil.copyOf(ByteUtil.toByteArray(0x00,0x20,0x40,0x80,0xFF), 1, 3)); } @Test public void toUnsignedByteTest() { assertEquals(0x00, ByteUtil.toUnsignedByte(0x00)); assertEquals(0x40, ByteUtil.toUnsignedByte(0x40)); assertEquals(0x80, ByteUtil.toUnsignedByte(0x80)); assertEquals(0xFFL, ByteUtil.toUnsignedByte(0xFF)); assertEquals(0xFFL, ByteUtil.toUnsignedByte(0xFFFFFFFF)); } @Test public void toUnsignedIntTest() { assertEquals(0x00, ByteUtil.toUnsignedInt(0x00)); assertEquals(0x40, ByteUtil.toUnsignedInt(0x40)); assertEquals(0x80, ByteUtil.toUnsignedInt(0x80)); assertEquals(0x000000FFL, ByteUtil.toUnsignedInt(0xFF)); assertEquals(0x0000FF00L, ByteUtil.toUnsignedInt(0xFF) << 8); assertEquals(0xFF000000L, ByteUtil.toUnsignedInt(0xFF) << 24); assertEquals(0x000000FFL, ByteUtil.toUnsignedInt(0xFF)); assertEquals(0x0000FF00L, ByteUtil.toUnsignedInt(0xFF) << 8); assertEquals(0x800000L, ByteUtil.toUnsignedInt(0x800000)); assertEquals(0x7FFFFFFFL, ByteUtil.toUnsignedInt(0x7FFFFFFF)); assertEquals(0x80000000L, ByteUtil.toUnsignedInt(0x80000000)); assertEquals(0xFFFFFFFFL, ByteUtil.toUnsignedInt(0xFFFFFFFF)); } @Test public void toByteArrayTest() { assertArrayEquals(new byte[] { (byte) (0xFF & 0xFF), (byte) (0x80 & 0xFF) }, ByteUtil.toByteArray(0xFF, 0x80)); assertArrayEquals(new byte[] { (byte) (0xFF & 0xFF), (byte) (0x80 & 0xFF), 0x40, 0x20 }, ByteUtil.toByteArray(0xFF, 0x80, 0x40, 0x20)); } @Test public void toInt4byteTest() { assertEquals(0x10203040, ByteUtil.toInt(new byte[] { 0x10, 0x20, 0x30, 0x40 }, true)); assertEquals(0x40302010, ByteUtil.toInt(new byte[] { 0x10, 0x20, 0x30, 0x40 }, false)); assertEquals( 0xFF804020, ByteUtil.toInt(ByteUtil.toByteArray(0xFF, 0x80, 0x40, 0x20), true)); assertEquals( 0x204080FF, ByteUtil.toInt(ByteUtil.toByteArray(0xFF, 0x80, 0x40, 0x20), false)); } @Test(expected = AssertionError.class) public void toIntTestNG1() { assertEquals(0x10203041, ByteUtil.toInt(new byte[] { 0x10, 0x20, 0x30, 0x40 }, true)); } @Test(expected = AssertionError.class) public void toIntTestNG2() { assertEquals(40302010, ByteUtil.toInt(new byte[] { 0x10, 0x20, 0x30, 0x40 }, false)); } @Test public void toFloatTest() { assertTrue(Float.valueOf(0.15625F).equals(ByteUtil.toFloat(ByteUtil.toByteArray(0x3E, 0x20, 0x00, 0x00), true))); assertTrue(Float.valueOf(1.5F).equals(ByteUtil.toFloat(ByteUtil.toByteArray(0x3F, 0xC0, 0x00, 0x00), true))); assertTrue(Float.valueOf(-4.9F).equals(ByteUtil.toFloat(ByteUtil.toByteArray(0xC0, 0x9C, 0xCC, 0xCD), true))); } @Test public void toBitStringTest() { assertEquals("00111110001000000000000000000000", ByteUtil.toBitString(ByteUtil.toByteArray(0x3E, 0x20, 0x00, 0x00))); } @Test public void toByteArrayByteTest() { assertEquals("11111111111111111111111111111111", ByteUtil.toBitString(ByteUtil.toByteArray(0xFFFFFFFF, true))); assertEquals("11111111111111111111111111111111", ByteUtil.toBitString(ByteUtil.toByteArray(0xFFFFFFFF, false))); assertEquals("00001111111111111111111111111111", ByteUtil.toBitString(ByteUtil.toByteArray(0x0FFFFFFF, true))); assertEquals("11111111111111111111111100001111", ByteUtil.toBitString(ByteUtil.toByteArray(0x0FFFFFFF, false))); assertEquals("00001111111111111111111111110000", ByteUtil.toBitString(ByteUtil.toByteArray(0x0FFFFFF0, true))); assertEquals("11110000111111111111111100001111", ByteUtil.toBitString(ByteUtil.toByteArray(0x0FFFFFF0, false))); } @Test public void toShortTest() { assertEquals(0x1020, ByteUtil.toShort(new byte[] { 0x10, 0x20 }, true)); assertEquals(0x2010, ByteUtil.toShort(new byte[] { 0x10, 0x20 }, false)); assertEquals( (short)0xFF80, ByteUtil.toShort(new byte[] { (byte) (0x000000FF & 0xFF), (byte) (0x00000080 & 0xFF) }, true)); assertEquals( (short) 0x80FF, ByteUtil.toShort(new byte[] { (byte) (((int) 0xFF) & 0xFF), (byte) (((int) 0x80) & 0xFF) }, false)); } @Test public void toLongTest() { assertEquals(0x1020304050607080L, ByteUtil.toLong(ByteUtil.toByteArray(0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80), true)); assertEquals(0x8070605040302010L, ByteUtil.toLong(ByteUtil.toByteArray(0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80), false)); assertEquals(0x10203040506070FFL, ByteUtil.toLong(ByteUtil.toByteArray(0x10,0x20,0x30,0x40,0x50,0x60,0x70,0xFF), true)); assertEquals(0xFF70605040302010L, ByteUtil.toLong(ByteUtil.toByteArray(0x10,0x20,0x30,0x40,0x50,0x60,0x70,0xFF), false)); } }