用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

news2024/12/27 14:41:33

写在前面

源码 。
本文一起看下方法调用相关的指令invokexxx以及方法返回(栈帧弹出线程栈)相关的指令xReturn 。

1:正文

因为invokexxx指令和普通的指令不同,会创建一个新的栈帧,并压倒操作数栈中,所以我们首先需要来定义一个公共的创建栈帧的方法来让ivvokestatic,invokeinterface等使用,如下:

public class MethodInvokeLogic {

    public static void invokeMethod(Frame invokerFrame, Method method) {
        if (method.name.equalsIgnoreCase("returnALong")) {
            System.out.println("------invoke xxxx 指令,做如下的事情;------");
            System.out.println("1:从当前栈帧中获取对应的执行线程");
            System.out.println("2:为要执行的方法创建对应的栈帧,并将栈帧压倒线程栈,作为当前栈帧");
            System.out.println("3:如果是有入参的话,则从调用栈帧的操作数栈中弹出入参,并设置到新栈帧的局部变量表中,这样被调用方法执行时就可以通过load指令从局部变量中获取操作");
        }
        Thread thread = invokerFrame.thread();
        Frame newFrame = thread.newFrame(method);
        thread.pushFrame(newFrame);
        // 将方法需要的参数设置到新栈帧的局部变量表中
        int argSlotCount = method.argSlotCount();
        if (argSlotCount > 0) {
            for (int i = argSlotCount - 1; i >= 0; i--) {
                Slot slot = invokerFrame.operandStack().popSlot();
                newFrame.localVars().setSlot(i, slot);
            }
        }

        //hack
        if (method.isNative()) {
            if ("registerNatives".equals(method.name())) {
                thread.popFrame();
            } else {
                throw new RuntimeException("native method " + method.name());
            }
        }
    }

}

这里我们以invokestatic指令和lreturn指令为例来看下对应的模拟代码,invokestatic指令:

public class INVOKE_STATIC extends InstructionIndex16 {

    @Override
    public void execute(Frame frame) {
        RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();
        MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
        Method resolvedMethod = methodRef.ResolvedMethod();

        if (!resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        Class clazz = resolvedMethod.clazz();
        // 确保初始化完成(加载,链接,初始化中的初始化,即类加载的最后一步)
        if (!clazz.initStarted()) {
            frame.revertNextPC();
            ClassInitLogic.initClass(frame.thread(), clazz);
            return;
        }
        // 执行方法(即生成栈帧并压入到线程栈)
        MethodInvokeLogic.invokeMethod(frame, resolvedMethod);
    }
}

lreturn:

public class LRETURN extends InstructionNoOperands {

    @Override
    public void execute(Frame frame) {
        System.out.println("------lreturn 指令执行,做如下的事情:------");
        System.out.println("1:弹出当前的方法栈帧");
        System.out.println("2:获取上一个方法");
        System.out.println("3:从当前方法的操作数栈中获取执行结果,并推送到上一个方法的操作数栈中");
        Thread thread = frame.thread();
        Frame currentFrame = thread.popFrame();
        Frame invokerFrame = thread.topFrame();
        long val = currentFrame.operandStack().popLong();
        // 获取上一个方法,并将结果压倒其操作数栈的栈顶,这样,上一个就可以通过pop指令获取结果
        invokerFrame.operandStack().pushLong(val);
    }

}

main类:

/**
 * -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();
    }

    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);
        }
    }*/

}

定义main的program argument:

-Xthejrepath
D:\programs\javas\java1.8/jre
-Xthetargetclazz
D:\test\itstack-demo-jvm-master\tryy-too-simulate-invokexxx-and-xreturn\target\test-classes\org\itstack\demo\test\HelloWorld

在这里插入图片描述
最后运行:
在这里插入图片描述

写在后面

参考文章列表

JVM 虚拟机字节码指令表 。

jvm方法调用指令invokestatic,invokespecial,invokeinterface,invokevirutal分析 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1984498.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《黑神话:悟空》在PS5上优化得不错 能达到2K/60帧

《黑神话&#xff1a;悟空》是今年最受玩家期待的游戏之一&#xff0c;但许多粉丝担心该作优化不佳&#xff0c;因为其使用的是虚幻5引擎。虚幻5引擎会导致性能问题出现&#xff0c;游戏 科学的新作也将面临同样问题。但有新报告称&#xff0c;《黑神话》PS5版优化得相当不错&a…

UE 后期处理

UE4后期处理材质的一些应用&#xff08;上&#xff09; - 哔哩哔哩 (bilibili.com) UE4后期处理材质的一些应用&#xff08;下&#xff09; - 哔哩哔哩 (bilibili.com) 后期处理材质的作用 后期处理材质使您能够设置与后期处理一起使用的材质&#xff0c;以创建破坏的视觉屏幕…

免费【2024】springboot 二手图书交易系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

PyCharm找不到Python了咋办

Python发生了重装的&#xff0c;且新的路径和原有路径不同&#xff0c;就会出现如下的错误&#xff1a; 解决办法&#xff1a; 点开PyCharm菜单的File/Setting 然后&#xff1a; 有上图的提示&#xff0c;说明需要将原来的venv进行清空。 如此操作之后&#xff0c;原来的红色…

交通预测数据文件梳理:PEMS04

文章目录 前言一、PEMS04.csv文件二、adj_PEMS04.pkl文件三、adj_PEMS04_distance.pkl文件四、PEMS04.npz文件 前言 最近做的实验比较多&#xff0c;对于交通预测数据的各种文件和文件中的数据格式理解愈加混乱&#xff0c;因此打算重新做一遍梳理来加深实验数据集的理解&…

【矩阵对角线求和】求一个3*3矩阵对角线元素之和

求一个3*3矩阵对角线元素之和&#xff0c;使用C语言实现 具体代码&#xff1a; #include<stdio.h>int main(){float a[3][3],sum0;printf("请输入3x3矩阵的元素&#xff08;按行输入&#xff09;&#xff1a;\n");for(int i0;i<3;i){for(int j0;j<3;j)…

AD 飞线显示混乱、错位

执行Design->Netlist->Update Free Primitives From Componet Pads

8月6(信息差)

&#x1f30d;华为最便宜小折叠&#xff01;华为nova Flip今晚发布&#xff1a;搭载麒麟8000芯片 从曝光的跑分信息来看&#xff0c;nova Flip将搭载麒麟8000处理器&#xff0c;也就是nova 12 Pro/Ultra的同款&#xff0c;采用8核心的134组合&#xff0c;大核是1颗2.4GHz的Cor…

如何用ai来完成数据库分析(1)

前言 因一些课程设计要写长篇分析报告&#xff0c;这里借用ai做一篇指导教程&#xff0c;分上下两篇。这篇也会教如何让ai给你你想要的答案&#xff0c;众所周知&#xff0c;现在的ai并不智能&#xff0c;不针对各类厂家&#xff0c;但是放出来的确实表象如此。 但其实问法决…

SAP ABAP代码模板CLASS

此模板也使用OO ALV,创建新程序简单&#xff0c;功能包装独立&#xff0c;用到一个独立的CLASS. 1.ALV类 class ZCL_CM_GUI_ALV definitionpublicfinalcreate public .public section.data REPID type SYREPID .data DYNNR type SYDYNNR .data TOOLBAR type CHAR30 .data USE…

Linux中的进程替换

一、理解进程替换 首先&#xff0c;exec* 系列函数能让进程执行新程序&#xff0c;上图我们用到的是 int execl(const char* path, const char* arg, ...)函数&#xff0c;所以相当于执行了 ls -la 指令&#xff0c;这就完成了进程的替换。 本来子进程中存放的是父进程的代码和…

5. 有效的括号

5. 有效的括号 题目题目分析 题目 题目分析 一个很标准的关于栈知识点的应用&#xff0c;首先先初始化一个栈&#xff0c;再遍历字符串s,当匹配到为左边字符串是将其压入栈中&#xff0c;遇到右边字符串时要判断此时的栈顶元素是否与其匹配&#xff0c;若匹配则将栈顶元素弹出&…

GPX格式详解,javascript写入读取GPX示例

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

如何使用Markdown编辑器

欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持&#x…

【PLC】关于子程序功能以及编程过程中的部分心得

博主在使用GX Works对三菱PLC编程的时候用到了子程序功能&#xff0c;这里将使用子程序功能中的一点心得以及编程过程中的部分心得分享给大家。 博主主要对以下几个问题有一些心得&#xff1a; 1、如何调试带有子程序的程序&#xff1f; 2、如何让程序按照计划的顺序去执行&…

【OpenCV C++20 学习笔记】自定义线性滤波-filter2D

自定义线性滤波 原理相关卷积核线性滤波操作 API实例 原理 相关 线性滤波的是指就是相关&#xff0c;即计算图像中的每个部分和卷积核(kernel)的相关结果。 卷积核 卷积核本质上是一个固定大小的系数数组&#xff0c;数组中的某个元素被作为锚点&#xff08;一般是数组的中…

C++之从C过渡(上)

C之从C过渡 前言 暂时告别C语言&#xff0c;我们走进C。对于有C语言基础&#xff0c;初学C的我们来说&#xff0c;在正式学习C的主体内容之前&#xff0c;我们需要先有一个过渡&#xff0c;本文中会总结过渡需要了解的零散知识&#xff0c;主要是语法。 正文 C的第一个程序 …

终于用PC串口显示出esp32 串口输出hello world

硬件&#xff1a; esp32模块 rs232 转ttl 3.3v 电平转换器 3.3v 外接电源 esp32 tx 脚接转换器rx, rx脚接转换器tx esp32 使用uart2 现在就可以用pc作为上位机通过串口控制esp32&#xff0c;用pc串口调试软件作为esp的输出监控器显示esp的各种运算结果。 #include &qu…

使用visual studio2019创建dll导出自定义类

系列文章目录 文章目录 系列文章目录前言一、具体操作步骤1.创建动态链接库工程(DLL)2.头文件声明3.实现文件定义4.生成dll工程5 使用dll总结 前言 程序对动态链接库dll、静态链接库lib想必都很熟悉了&#xff0c;网上也有很多的相关介绍。但网上介绍的一般都是C语言函数介绍&…

javascript 的奇技巧淫二

文章目录 1 、标记模板文字2、使用 Object.entries() 和 Object.fromEntries()3、用于唯一元素的 Set 对象4、对象中的动态属性名称5、使用 bind() 进行函数柯里化6、使用 Array.from() 从类似数组的对象创建数组7、可迭代对象的 for…of 循环8、使用 Promise.all() 实现并发 P…