JAVA字节码编写1到100的整数计算

1.代码实例

1.1 主类
package asm.bytecode;

import asm.create.bean.MyClassLoader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static org.objectweb.asm.Opcodes.*;

/**
 * public class ForAsm {
 *
 *    public ForAsm() {
 *        super();
 *    }
 *    int sum = 0;
 *    for(int i=0; i<=100; i++) {
 *      sum += i;
 *   }
 * }
 * return sum;
 * @date 2018/12/29 23:58
 */
public class ForAsmMain {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        String fullName = ClassUtil.buildFullName("ForAsm");
        String fullNameType = fullName.replace(".","/");
        /**
         * ClassWriter参数:
         *  0:自己计算操作数栈和局部变量栈
         */
        ClassWriter cw = new ClassWriter(0);
        ByteCodeUtil byteCodeUtil = new ByteCodeUtil(cw,fullNameType);
        byteCodeUtil.createClass(null);
        byteCodeUtil.generatorConstructor(null);
        /**
         * public int spain() {
         *      int sum = 0;
         *      for(int i=0; i<=100; i++) {
         *          sum += i;
         *      }
         *      return sum;
         * }
         */
        MethodVisitor spainMethod = cw.visitMethod(ACC_PUBLIC, "spain", "()I", null, null);
        spainMethod.visitCode();
        //下面两条指令: int sum = 0
        spainMethod.visitInsn(ICONST_0);
        spainMethod.visitVarInsn(ISTORE,1);
        //for循环的开始标记
        Label forLabel = new Label();
        //for循环的结束标记
        Label endLabel = new Label();
        //变量索引0的位置存放了this
        //int i = 0;
        spainMethod.visitInsn(ICONST_0);
        spainMethod.visitVarInsn(ISTORE,2);
        //做了一个用于跳转的标记
        spainMethod.visitLabel(forLabel);
        //从索引为1的位置取出变量
        spainMethod.visitVarInsn(ILOAD,2);
        //将常量100放入操作数栈中
        spainMethod.visitVarInsn(BIPUSH,100);
        //当i<=100时,执行
        spainMethod.visitJumpInsn(IF_ICMPGT,endLabel);
        //做一些逻辑操作
        spainMethod.visitVarInsn(ILOAD,1);
        spainMethod.visitVarInsn(ILOAD,2);
        spainMethod.visitInsn(IADD);
        spainMethod.visitVarInsn(ISTORE,1);
        //将局部变量表中索引为1的数值加1,做i++操作
        spainMethod.visitIincInsn(2,1);
        spainMethod.visitJumpInsn(GOTO,forLabel);
        spainMethod.visitLabel(endLabel);
        //要返回一个数据,只需要将该数据放入到操作数栈栈顶
        spainMethod.visitVarInsn(ILOAD,1);
        spainMethod.visitInsn(IRETURN);
        //计算操作数栈和局部变量表的大小,对于操作数栈,任何读取操作数的指令,都会将数据POP(弹出栈)
        spainMethod.visitMaxs(2,3);
        spainMethod.visitEnd();
        byte[] bytes = byteCodeUtil.writeToFile(null);
        MyClassLoader classLoader = new MyClassLoader();
        System.out.println(fullNameType.replace("/","."));
        Class<?> cls = classLoader.defineClassPublic(fullNameType.replace("/","."),bytes,0,bytes.length);
        Object o = cls.newInstance();
        Method spain = cls.getMethod("spain");
        Object ret = spain.invoke(o);
        System.out.println(ret);
    }
}
1.2 帮助生成字节码的工具类
package asm.bytecode;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import static org.objectweb.asm.Opcodes.*;
/**
 * 字节码相关的操作工具类
 * @date 2018/12/30 0:12
 */
public class ByteCodeUtil {

    private ClassWriter cw;

    private String classNameType;

    public ByteCodeUtil(ClassWriter cw,String classNameType) {
        this.cw = cw;
        this.classNameType = classNameType;
    }

    /**
     * 创建类
     * public class Demo {
     *
     *     private String deveceName;
     *     private String deviceType;
     *     private Integer deviceSize;
     *     ....
     * }
     * @param typeNameMap
     */
    public void createClass(Map<String,Class<?>> typeNameMap) {
        cw.visit(V1_7, ACC_PUBLIC, classNameType, null, "java/lang/Object", null);
        if (typeNameMap != null && typeNameMap.size() != 0) {
            for (Map.Entry<String, Class<?>> entry : typeNameMap.entrySet()) {
                cw.visitField(ACC_PRIVATE, entry.getKey(), Type.getDescriptor(entry.getValue()), null, null).visitEnd();
            }
        }
    }

    /**
     * 生成构造函数
     * nameValueMap == null:
     * public Demo() {
     *     super();
     * }
     * 不空:
     * public Demo(String deveceName,String deviceType,Integer deviceSize...) {
     *      super.Object();
     *      this.deveceName = deveceName;
     *      this.deviceType = deviceType;
     *      this.deviceSize = deviceSize;
     *      ......
     * }
     * @param nameValueMap key:成员属性的名称,value:初始化的值
     */
    public void generatorConstructor(Map<String,Object> nameValueMap){
        MethodVisitor constructorMethod = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        constructorMethod.visitCode();
        constructorMethod.visitIntInsn(ALOAD,0);
        constructorMethod.visitMethodInsn(INVOKESPECIAL,"java/lang/Object","<init>","()V",false);
        //带有参数的构造函数
        boolean hasParam = nameValueMap != null && nameValueMap.size() != 0;
        if(hasParam) {
            // todo 设置参数
        }
        constructorMethod.visitInsn(RETURN);
        //手动维护栈大小和局部变量表的大小
        constructorMethod.visitMaxs(1,1);
        if(hasParam) {
            // todo 重新计算栈大小和局部变量表的大小
        }
        constructorMethod.visitEnd();
    }

    /**
     * 将字节码数据写入到文件
     * @param pathName 要写入的文件路径,目前为空
     * @return
     */
    public byte[] writeToFile(String pathName) {
        colseClassWrite();
        byte[] bytes = null;
        if(StringUtils.isEmpty(pathName)) {
            File file = new File("F:\\out.class");
            try (
                    FileOutputStream fos = new FileOutputStream(file);
            ) {
                bytes = cw.toByteArray();
                fos.write(bytes);
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return bytes;
    }

    public void colseClassWrite() {
        cw.visitEnd();
    }

}
1.3 类操作相关的工具类
package asm.bytecode;
/**
 * 类操作工具类
 * @date 2018/12/30 20:44
 */
public class ClassUtil {
    /**
     * 构建全限定名,即包名+类名:asm.bytecode.Demo
     * @param className
     * @return
     */
    public static String buildFullName(String className) {
        String packagePath = ClassUtil.class.getName();
        return packagePath + "." + className;
    }
    /**
     * 构建类的路径名,即asm/bytecode/Demo
     */
    public static String buildFullNameType(String className) {
        return buildFullName(className).replace(".","/");
    }
}

2.运行结果

在这里插入图片描述

3.出现的问题

在这里插入图片描述

4. 解决方案

java8添加该参数: -noverify
java7添加该参数:-XX:-UseSplitVerifier
在这里插入图片描述

源码github地址:https://github.com/wjyGithub/ASMLearn
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值