ASM Java字节码操作框架入门学习 输出Hello World

ASM Java字节码操作框架入门学习 输出Hello World

1.类信息

package org.example;

public class Hello {

    public void say(){
        System.out.println("hello world");
    }

}

查看字节码信息

image-20230709204204625

 			//动态设置栈大小
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

       //设置类的基本信息
        classWriter.visit(
                V1_8, //设置JDK版本,
                ACC_PUBLIC, // 设置权限修饰符,
                "Hello", //新类的权限定类名,
                null,//泛型
                "java/lang/Object", // 父类
                null//实现的接口
        );

2.无参构造方法

image-20230709204353627

0 aload_0
1 invokespecial #1 <java/lang/Object.<init> : ()V>
4 return

  			MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        constructor.visitVarInsn(Opcodes.ALOAD, 0); //操作局部变量表 局部变量表第一个存放this
        constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        constructor.visitInsn(Opcodes.RETURN); // 调用  return 指令
        constructor.visitMaxs(1, 1);//方法的最大栈大小  方法的最大局部变量数
        constructor.visitEnd(); //方法结束

3.say 方法

image-20230709204937753

0 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
3 ldc #3 <hello world>
5 invokevirtual #4 <java/io/PrintStream.println : (Ljava/lang/String;)V>
8 return
			//添加say方法
        MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC,//方法修饰符号
                "say",//方法名
                "()V",//方法的描述符,用于描述方法的参数类型和返回值类型
                null,//方法泛型
                null//可能抛出的异常
        );

        //获取System.out
        methodVisitor.visitFieldInsn(
                Opcodes.GETSTATIC,//字段的类型
                "java/lang/System",//字段所属类的全限定类名
                "out",//指定字段
                "Ljava/io/PrintStream;"//字段描述信息
        );
        
        //加载常量
        methodVisitor.visitLdcInsn("hello world");

				
         methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,//方法调用的操作码   
                "java/io/PrintStream",//方法的权限定类名
                "println",//方法名
                "(Ljava/lang/String;)V", //方法修饰符
                false);//方法调用者是否是接口
                
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 0);
        methodVisitor.visitEnd();

4.完成类的定义并创建实例调用目标方法

  			// 完成类的定义
        classWriter.visitEnd();
        // 将生成的字节码写入文件或加载到内存中
        byte[] bytecode = classWriter.toByteArray();


        ClassLoader classLoader = new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                return defineClass(name, bytecode, 0, bytecode.length);
            }
        };
        // 加载并实例化Hello类
        Class<?> helloClass = classLoader.loadClass("Hello");
        Object helloObject = helloClass.getDeclaredConstructor().newInstance();
        // 调用say方法
        helloClass.getMethod("say").invoke(helloObject);

5.相关JVM指令

方法调用

  1. invokestatic:用于调用静态方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。
  2. invokespecial:用于调用私有方法、构造方法和父类方法。该指令会根据方法的类名、方法名和方法描述符进行方法查找和调用。
  3. invokevirtual:用于调用实例方法。该指令会根据对象的类型和方法的签名进行方法查找和调用。
  4. invokeinterface:用于调用接口方法。该指令会根据接口的类型和方法的签名进行方法查找和调用。
  5. invokedynamic:用于调用动态方法。该指令会通过调用动态绑定方法来实现方法的调用

加载常量或数字

  1. ldc:将常量(包括字符串、整数、浮点数等)加载到操作数栈上。
  2. ldc_w:与ldc类似,但用于加载较大的常量(超过65535个字节)。
  3. bipush:将一个字节大小的整数常量(-128到127之间)加载到操作数栈上。
  4. sipush:将一个短整型常量(-32768到32767之间)加载到操作数栈上。
  5. iconst_:将整数常量(-1到5之间)加载到操作数栈上,其中为0到5之间的数字。 _
  6. fconst:将浮点数常量(0.0、1.0和2.0)加载到操作数栈上,其中为0到2之间的数字。
  7. dconst_:将双精度浮点数常量(0.0和1.0)加载到操作数栈上,其中为0或1。 _
  8. _ lconst_:将长整型常量(0和1)加载到操作数栈上,其中为0或1

猜你喜欢

转载自blog.csdn.net/JAVAlife2021/article/details/131627433