asm操作Java(二)


1.クラス:
a)配列:
i.作成:
mv.visitInsn(ICONST_3);
mv.visitIntInsn(NEWARRAY, T_INT);
mv.visitVarInsn(ASTORE, 1);      //               1    

次のように等価です.
int[] a = new int[3];

ii.取値:
mv.visitVarInsn(ALOAD, 1);       //             1    
mv.visitInsn(ICONST_1);
mv.visitInsn(IALOAD);
mv.visitVarInsn(ISTORE, 2);

次のように等価です.
int b = a[1];
iii.付与:
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_2);
mv.visitInsn(IASTORE);

次のように等価です.
a[1] = 2;

b)構築関数:
i. :
1.作成:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", ()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();

説明:コンストラクション関数実行時には、まず親クラスのコンストラクション関数またはクラス内部の他の構造を実行する必要があります.
で行ないます.
2.呼び出し:
mv.visitTypeInsn(NEW, "asm/A");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "asm/A", "<init>", "()V");
mv.visitVarInsn(ASTORE, 1);

次のように等価です.
A a = new A();

説明:一般オブジェクトを初期化する場合は、まずNEWコマンドを呼び出してオブジェクトインスタンスを作成する必要があります.によって
後続のINVOKESPECIAL命令は呼び出しクラスのコンストラクション関数であり、その命令が実行された後、オブジェクトへの引数
スタックからポップアップするので、NEW命令が実行された後、INVOKESPECIAL命令が実行される前に、
オブジェクト参照のコピーを増やすには、DUPコマンドを呼び出す必要があります.
ii. :
1.作成:
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("hello world");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 0);
mv.visitEnd();

次のように等価です.
static {
System.out.println("hello world");
}

2.呼び出し:クラスがロードされたときに自動的に呼び出されます.
c)フィールド:
i.一般フィールド:
1.作成:
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "a", "I", null, null);
fv.visitEnd();

次のように等価です.
private int a;

2.読み出し:読み出しクラスの名前がa、タイプがintのフィールドの値.
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "asm/A", "a", "I");

3.設定:
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTFIELD, "asm/A", "a", "I");

次のように等価です.
a = 2;

ii.静的フィールド:
1.作成:
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "a", "I", null, null);
fv.visitEnd();

次のように等価です.
private static int a;

2.読み込み:
mv.visitFieldInsn(GETSTATIC, "asm/A", "a", "I");

3.設定:
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTSTATIC, "asm/A", "a", "I");

次のように等価です.
a = 2;

d)方法:
i.インタフェース方法:
1.定義:
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "getA", "()V", null, null);
mv.visitEnd();

2.呼び出し:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "asm/IA", "getA", "()V");

次のように等価です.
public interface IA{
     public void geA();
}
public class A implements IA{
     public void geA(){…}
}

IA a = new A();
a.getA();

ii.一般的な方法:
1.定義:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getA", "()V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 1);
mv.visitEnd();

次のように等価です.
public void getA() {}

2.呼び出し:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "asm/A", "getA", "()V");

次のように等価です.
A a = new A():
a.getA();

iii.静的方法:
1.定義:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getA", "()V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();

次のように等価です.
public static void getA() {}

2.呼び出し:
mv.visitMethodInsn(INVOKESTATIC, "asm/A", "getB", "()V");

次のように等価です.
A.getB();

iv.説明:一般的なメソッドは、静的メソッドよりも宣言および呼び出し時にthis参照をパラメータとして入力します.また、INVOKESPECIALを使用してメソッドを呼び出すと、仮想マシンは命令で指定されたクラスタイプに直接メソッドを呼び出す.一方、INVOKEVIRTUALを使用してメソッドを呼び出すと、仮想マシンはインスタンスの実際のタイプに基づいてメソッドを呼び出す.
e)異常処理:
i.声明:
mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");
mv.visitLabel(l0);
mv.visitLabel(l1);
…

次のように等価です.
try {
…
} catch (Exception e) {
…
}

説明:visitTryCatchBlock()では、第1、第2、3つのパラメータがLabelインスタンスであり、そのうちの1、2はtryブロックの範囲を表し、3はcatchブロックの開始位置である.4番目のパラメータは例外タイプです.異常が発生すると、JVMは異常インスタンスを実行スタックのスタックトップに配置します.