ちょっとバイトコードを生成してみる

バイトコードを生成してみました。
ここでは、次のようなコードを生成しています。

public class ByteCodeGenerator{
  public static int eval(){
    return 34;
  }
}


なので、下のプログラムを実行すると34が表示されます。
クラスファイルの仕様はここ
Java SE Specifications
バイトコードの一覧は、Wikipediaがアルファベット順になってて見やすい
Java bytecode - Wikipedia

import java.io.*;

public class ByteCode {
   public static void main(String[] a) throws Exception{
        String className = "ByteCodeGenerate";
        String methodName = "eval";
       
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(buf);
        dos.writeInt(0xcafebabe);//クラスファイルを識別するマジックナンバー
        dos.writeShort(0);//マイナーバージョン
        dos.writeShort(49);//メジャーバージョン
        
        //定数の出力
        ByteArrayOutputStream constbuf = new ByteArrayOutputStream();
        DataOutputStream constos = new DataOutputStream(constbuf);
        int id = 1;
        int idClassName = id++;
        constos.writeByte(1);//識別子
        constos.writeUTF(className);
        int idMethod = id++;
        constos.writeByte(1);//識別子
        constos.writeUTF(methodName);
        int idType = id++;
        constos.writeByte(1);//識別子
        constos.writeUTF("()I");
        int idObj = id++;
        constos.writeByte(1);//識別子
        constos.writeUTF("java/lang/Object");
        int idClassObj = id++;
        constos.writeByte(7);//クラス
        constos.writeShort(idObj);
        int idClass = id++;
        constos.writeByte(7);//クラス
        constos.writeShort(idClassName);
        int idCode = id++;
        constos.writeByte(1);//識別子
        constos.writeUTF("Code");//コード用の属性名
        constos.close();
        constbuf.close();
        
        dos.writeShort(id);//定数の個数(実際の個数より1多くする)
        dos.write(constbuf.toByteArray());

        //クラス
        dos.writeShort(0x21);// アクセス指定 ACC_SUPER(0x20) | ACC_PUBLIC(0x01)
        dos.writeShort(idClass);//クラス
        dos.writeShort(idClassObj);//スーパークラス
        dos.writeShort(0);//インタフェースの個数
        dos.writeShort(0);//フィールドの数
        dos.writeShort(1);//メソッドの数
        
        //メソッド
        dos.writeShort(9);// アクセス指定 ACC_PUBLIC(0x01) | ACC_STATIC(0x08)
        dos.writeShort(idMethod);//名前
        dos.writeShort(idType);//シグネチャ
        dos.writeShort(1);//属性の数(ここではメソッド本体)
        dos.writeShort(idCode);//属性名(Code)
        dos.writeInt(15);//属性の長さ
        dos.writeShort(1);//最大スタック
        dos.writeShort(0);//最大変数
        dos.writeInt(3);//コードのバイト数
        //メソッド
        dos.writeByte(0x10);//bipush
        dos.writeByte(34);
        dos.writeByte(0xac);//ireturn;
        //メソッドおわり
        dos.writeShort(0);//例外の数
        dos.writeShort(0);//属性の数
        
        dos.writeShort(0);//属性の数
        
        dos.close();
        buf.close();
        
        //クラスファイルを読み込んで実行
        final byte[] code = buf.toByteArray();
        ClassLoader cl = new ClassLoader(){
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                return defineClass(null, code, 0, code.length);
            }
        };
        Class expClass = cl.loadClass(className);
        int ret = (Integer)expClass.getMethod(methodName).invoke(null, new Object[0]);
        System.out.println(ret);
    }
}