用Java手写jvm之模拟数组相关操作

发布于:2024-08-08 ⋅ 阅读:(103) ⋅ 点赞:(0)

写在前面

本文看下如何模拟数组相关的操作,主要是实现数组相关的指令,关于数组相关的指令可以参考这篇文章

1:正文

简单起见这里我们仅仅实现int基础数据类型的一维数组。

  • newarray指令对应的类
package com.dahuyou.tryy.too.simulate.interpreter.instructions.references;

import com.dahuyou.tryy.too.simulate.interpreter.instructions.base.BytecodeReader;
import com.dahuyou.tryy.too.simulate.interpreter.instructions.base.Instruction;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.Frame;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.OperandStack;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.ClassLoader;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Class;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Object;

public class NEW_ARRAY implements Instruction {

    private byte atype;

    @Override
    public void fetchOperands(BytecodeReader reader) {
        this.atype = reader.readByte();
    }

    @Override
    public void execute(Frame frame) {
        // 获取操作数栈
        OperandStack stack = frame.operandStack();
        // 弹出操作数栈栈顶的int类型整数,作为将要创建的数组的大小
        int count = stack.popInt();
        if (count < 0) {
            throw new NegativeArraySizeException();
        }

        // 获取类加载器
        ClassLoader classLoader = frame.method().clazz().loader();
        Class arrClass = getPrimitiveArrayClass(classLoader, this.atype);
        Object arr = arrClass.newArray(count);
        stack.pushRef(arr);

    }
    // ...
}
  • iastore指令对应的类
/**
 * 将栈顶的int类型数据存储到指定数组的指定位置
 * 即给int数组某位置元素赋值😂😂😂
 */
public class IASTORE extends InstructionNoOperands {

    @Override
    public void execute(Frame frame) {
        OperandStack stack = frame.operandStack();
        // 操作数栈中弹出一个元素作为要设置的目标值
        int val = stack.popInt();
        // 操作数栈中弹出一个元素作为要设置的元素的位置
        int idx = stack.popInt();
        System.out.println("IASTORE指令:从操作数栈栈中弹出要设置的元素和位置");
        Object arrRef = stack.popRef();

        checkNotNull(arrRef);
        // 这里是通过将真正的数组数据存储到Object中来模拟的,所以还是从Object中来获取,注意这里的Object是我们自定义的模拟的Object,不是jdk的java.lang.Object
        int[] ints = arrRef.ints();
        checkIndex(ints.length, idx);
        // 完成数组赋值,IASTORE指令工作done!
        ints[idx] = val;
        System.out.println("IASTORE指令:完成数组元素赋值");
    }

}
  • iaload对应的类
public class IALOAD extends InstructionNoOperands {

    @Override
    public void execute(Frame frame) {
        OperandStack stack = frame.operandStack();
        // 从操作数栈中弹出要获取元素的数组索引位置
        int idx = stack.popInt();
        Object arrRef = stack.popRef();

        checkNotNull(arrRef);
        int[] ints = arrRef.ints();
        checkIndex(ints.length, idx);
        // 从指定的索引位置获取数组元素,并将获取到的元素要到操作数栈的栈顶
        stack.pushInt(ints[idx]);
        System.out.println("IALOAD指令:从操作数栈中弹出要获取元素的数组索引位置");
        System.out.println("IALOAD指令:从指定的索引位置获取数组元素,并将获取到的元素要到操作数栈的栈顶");
    }

}
  • arraylength指令
public class ARRAY_LENGTH extends InstructionNoOperands {

    @Override
    public void execute(Frame frame) {

        OperandStack stack = frame.operandStack();
        Object arrRef = stack.popRef();
        if (null == arrRef){
            throw new NullPointerException();
        }

        int arrLen = arrRef.arrayLength();
        stack.pushInt(arrLen);
        System.out.println("ARRAY_LENGTH: 指令执行,获取数组长度并将结果压倒操作数栈的栈顶");
    }

}
  • 测试
    main测试类:
package com.dahuyou.tryy.too.simulate.interpreter;

import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.classpath.Classpath;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.ClassFile;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.MemberInfo;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.cmd.Cmd;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.ClassLoader;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Class;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Method;

/**
 * -Xthejrepath     D:\programs\javas\java1.8/jre -Xthetargetclazz     D:\test\itstack-demo-jvm-master\tryy-too-simulate-classload-load-clazz\target\test-classes\org\itstack\demo\test\HelloWorld
 */
public class Main {

    public static void main(String[] args) {
        Cmd cmd = Cmd.parse(args);
        if (!cmd.ok || cmd.helpFlag) {
            System.out.println("Usage: <main class> [-options] class [args...]");
            return;
        }
        if (cmd.versionFlag) {
            //注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jar
            System.out.println("java version \"1.8.0\"");
            return;
        }
        startJVM(cmd);
    }

    private static void startJVM(Cmd cmd) {
        // 创建classpath
        Classpath cp = new Classpath(cmd.thejrepath, cmd.classpath);
//        System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());
        System.out.printf("classpath:%s parsed class:%s \n", cp, cmd.thetargetclazz);
        //获取className
//        String className = cmd.getMainClass().replace(".", "/");
        try {
//            byte[] classData = cp.readClass(className);
            /*byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));
            System.out.println(Arrays.toString(classData));
            System.out.println("classData:");
            for (byte b : classData) {
                //16进制输出
                System.out.print(String.format("%02x", b & 0xff) + " ");
            }*/
            // 创建类加载器准备加载类
            /**
             * 加载3个阶段
             * 1:加载
             *      找到字节码,并将其存储到原元空间(<=7方法区),然后该类,该类父类,父接口也加载并在堆中生成对应的Class对象
             * 2:链接
             *      验证:验证文件内容的合法性,如是否cafebabe打头,结构是否符合定义
             *      准备:主要是给静态变量申请内存空间,以及赋初始值,如int,short这种则给默认值0
             *      解析:符号引用(指向类或者方法的一个字符串)转换为直接引用(jvm的内存地址)
             * 3:初始化
             *      执行<init>,<clinit>方法,完成静态变量的赋值
             */
            ClassLoader classLoader = new ClassLoader(cp);
            String clazzName = cmd.thetargetclazz.replace(".", "/");
            Class mainClass = classLoader.loadClass(clazzName);
            Method mainMethod = mainClass.getMainMethod();
            new Interpreter(mainMethod, true);


            /*// 创建className对应的ClassFile对象
            ClassFile classFile = loadClass(clazzName, cp);
            MemberInfo mainMethod = getMainMethod(classFile);
            if (null == mainMethod) {
                System.out.println("Main method not found in class " + cmd.classpath);
                return;
            }
            // 核心重点代码:通过解释器来执行main方法
            new Interpreter(mainMethod);*/
        } catch (Exception e) {
            System.out.println("Could not find or load main class " + cmd.getMainClass());
            e.printStackTrace();
        }
    }

    /**
     * 获取main函数,这里我们要模拟是执行器执行main函数的过程,当然其他方法也是一样的!!!
     * @param classFile
     * @return
     */
    private static MemberInfo getMainMethod(ClassFile classFile) {
        if (null == classFile) return null;
        MemberInfo[] methods = classFile.methods();
        for (MemberInfo m : methods) {
            if ("main".equals(m.name()) && "([Ljava/lang/String;)V".equals(m.descriptor())) {
                return m;
            }
        }
        return null;
    }

    /**
     * 生成class文件对象
     * @param clazzName
     * @param cp
     * @return
     */
    private static ClassFile loadClass(String clazzName, Classpath cp) {
        try {
            // 获取类class对应的byte数组
            byte[] classData = cp.readClass(clazzName);
            return new ClassFile(classData);
        } catch (Exception e) {
            System.out.println("无法加载到类: " + clazzName);
            return null;
        }
    }

}

定义需要解析的类:

public class HelloWorld {

    public static void main(String[] args) {
      /*  long x = fibonacci(10);
        System.out.println(x);*/
//        returnALong();
        // new int[8]; newarray指令
        int[] intArr = new int[8];
        // iastore指令
        intArr[0] = 89;
        // iaload指令
        int pos0 = intArr[0];
        // arraylength指令
        int arraylength = intArr.length;
    }

    public static long returnALong() {
        long longResult = 99;
        return longResult;
    }
    //斐波那契数列(Fibonacci sequence)
    /*private static long fibonacci(long n) {
        if (n <= 1) {
            return n;
        } else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }*/

}

配置program argument:
在这里插入图片描述
运行:
在这里插入图片描述

写在后面

参考文章列表

Java数组的类名是什么以及数组相关操作的指令有什么?


网站公告

今日签到

点亮在社区的每一天
去签到