目标
public class HelloWorld {
public void test(int a, int b) {
int val = Math.max(a, b); // 对static方法进行调用
System.out.println(val); // 对non-static方法进行调用
}
}
编码实现
import lsieun.utils.FileUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
public class HelloWorldGenerateCore {
public static void main(String[] args) throws Exception {
String relative_path = "sample/HelloWorld.class";
String filepath = FileUtils.getFilePath(relative_path);
// (1) 生成byte[]内容
byte[] bytes = dump();
// (2) 保存byte[]到文件
FileUtils.writeBytes(filepath, bytes);
}
public static byte[] dump() throws Exception {
// (1) 创建ClassWriter对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// (2) 调用visitXxx()方法
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld", null, "java/lang/Object", null);
{
MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv1.visitCode();
mv1.visitVarInsn(ALOAD, 0);
mv1.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv1.visitInsn(RETURN);
mv1.visitMaxs(1, 1);
mv1.visitEnd();
}
{
MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, "test", "(II)V", null, null);
mv2.visitCode();
mv2.visitVarInsn(ILOAD, 1);
mv2.visitVarInsn(ILOAD, 2);
mv2.visitMethodInsn(INVOKESTATIC, "java/lang/Math", "max", "(II)I", false);
mv2.visitVarInsn(ISTORE, 3);
mv2.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv2.visitVarInsn(ILOAD, 3);
mv2.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
mv2.visitInsn(RETURN);
mv2.visitMaxs(2, 4);
mv2.visitEnd();
}
cw.visitEnd();
// (3) 调用toByteArray()方法
return cw.toByteArray();
}
}
验证结果
import java.lang.reflect.Method;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("sample.HelloWorld");
Object obj = clazz.newInstance();
Method m = clazz.getDeclaredMethod("test", int.class, int.class);
m.invoke(obj, 10, 20);
}
}
Frame的变化
对于HelloWorld类中test()方法对应的Instruction内容如下:
public void test(int, int);
Code:
0: iload_1
1: iload_2
2: invokestatic #21 // Method java/lang/Math.max:(II)I
5: istore_3
6: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
9: iload_3
10: invokevirtual #33 // Method java/io/PrintStream.println:(I)V
13: return
该方法对应的Frame变化情况如下:
test(II)V
[sample/HelloWorld, int, int] []
[sample/HelloWorld, int, int] [int]
[sample/HelloWorld, int, int] [int, int]
[sample/HelloWorld, int, int] [int]
[sample/HelloWorld, int, int, int] []
[sample/HelloWorld, int, int, int] [java/io/PrintStream]
[sample/HelloWorld, int, int, int] [java/io/PrintStream, int]
[sample/HelloWorld, int, int, int] []
[] []
小结
- 从Instruction的角度来讲,调用static方法是使用invokestatic指令,调用non-static方法一般使用invokevirtual指令。
- 从Frame的角度来讲,实现方法的调用,需要先将this变量和方法接收的参数放到operand stack上。
网友评论