18 - MethodVisitor创建对象
2022-01-27 本文已影响0人
舍是境界
目标
假如有一个GoodChild类,内容如下:
public class GoodChild {
public String name;
public int age;
public GoodChild(String name, int age) {
this.name = name;
this.age = age;
}
}
预期目标是生成一个HelloWorld类:
public class HelloWorld {
public void test() {
GoodChild child = new GoodChild("Lucy", 8);
}
}
编码实现
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", "()V", null, null);
mv2.visitCode();
mv2.visitTypeInsn(NEW, "sample/GoodChild");
mv2.visitInsn(DUP);
mv2.visitLdcInsn("Lucy");
mv2.visitIntInsn(BIPUSH, 8);
mv2.visitMethodInsn(INVOKESPECIAL, "sample/GoodChild", "<init>", "(Ljava/lang/String;I)V", false);
mv2.visitVarInsn(ASTORE, 1);
mv2.visitInsn(RETURN);
mv2.visitMaxs(4, 2);
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");
m.invoke(obj);
}
}
Frame的变化
对于HelloWorld类中test()方法对应的Instruction内容如下:
public void test();
Code:
0: new #11 // class sample/GoodChild
3: dup
4: ldc #13 // String Lucy
6: bipush 8
8: invokespecial #16 // Method sample/GoodChild."<init>":(Ljava/lang/String;I)V
11: astore_1
12: return
该方法对应的Frame变化情况如下:
test()V
[sample/HelloWorld] []
[sample/HelloWorld] [uninitialized_sample/GoodChild]
[sample/HelloWorld] [uninitialized_sample/GoodChild, uninitialized_sample/GoodChild]
[sample/HelloWorld] [uninitialized_sample/GoodChild, uninitialized_sample/GoodChild, java/lang/String]
[sample/HelloWorld] [uninitialized_sample/GoodChild, uninitialized_sample/GoodChild, java/lang/String, int]
[sample/HelloWorld] [sample/GoodChild]
[sample/HelloWorld, sample/GoodChild] []
[] []
小结
- 从Instruction的角度来讲,创建对象的指令集合:
- new
- dup
- invokespecial
- 从Frame的角度来讲,在创建新对象的时候,执行new指令之后,它是uninitialized状态,执行invokespecial指令之后,它是一个“合格”的对象。