自制Java虚拟机(三)运行第一个main函数

本文详细介绍了自制Java虚拟机执行指令的一般模型,如何寻找并执行main方法,以及如何逐步实现包括加减乘除在内的部分指令。通过一个计算1到100平均值的示例程序,验证了虚拟机的功能,展示了从字节码到实际运算的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自制Java虚拟机(三)运行第一个main函数

一、执行指令的一般模型

Java虚拟机有200多条指令,用switch-case来一一匹配并执行每个指令,显得过于臃肿又不灵活。我们可以把每个指令用一个函数实现,遇到指令就调用相应的函数处理之。这个函数应该知道它所处理指令的上下文,包括当前指令位置、当前类、当前帧等,这些我们都封装在一个结构体内,通过指针传给函数。函数太多,我们把它们组织到一个数组里,以opcode的数值作为索引,因为除最后2条指令外,前203条指令都是连续的。目前为了方便调试,把处理指令的函数又放到了结构体内。如下:

typedef void Opreturn;
typedef Opreturn (*InstructionFun)(OPENV *env); // 处理指令的函数原型(这里定义一个函数指针)
typedef struct _Instruction {
    const char *code_name;       // 该条指令opcode的助记符
    InstructionFun pre_action;   // 预处理代码(主要是大端转小端)
    InstructionFun action;       // 实际的指令实现
} Instruction;

OPENV是指令上下文,定义为:

typedef uchar* PC;
typedef struct _OPENV {
    PC pc; // 传说中的程序计数器,这里实际上是指向代码的当前执行位置
    PC pc_end;
    PC pc_start;
    StackFrame *current_stack;
    Class *current_class;
    Object *current_obj;
    method_info* method;
} OPENV;

至少需要pc(保存当前代码指针位置)、current_stack(当前帧/栈帧)和current_class(当前类)等字段,其它字段方便调试用。

把指令处理相关的函数放在数组里:

Instruction jvm_instructions[202] = { // 暂不考虑保留指令
    {
  "nop", pre_nop, do_nop},
    {
  "aconst_nul", pre_aconst_nul, do_aconst_nul},
    {
  "iconst_m1", pre_iconst_m1, do_iconst_m1},
    {
  "iconst_0", pre_iconst_0, do_iconst_0},
    ...
    {
  "jsr_w", pre_jsr_w, do_jsr_w}
};   

这些都很有规律,可以写个脚本来生成。

然后就是执行一个方法里面的代码了,大致如下:

void runMethod(OPENV *env)
{
    uchar op;
    Instruction instruction;
    do {
        op = *(env->pc);  // 取指令的opcode
        instruction = jvm_instructions[op]; // 取对应的实现函数
        printf("#%d: %s ", env->pc-env->pc_start, instruction.code_name);
        env->pc=env->pc+1; // 移到下个位置(可能是该条指令的操作码,也可能是下一条指令)
        instruction.action(env); // 执行指令
        printf("\n");
    } while(1);
}

跟现实世界CPU的执行指令的流程有点像。这是个死循环,不过不用担心,在return系列指令的实现里自有办法处理。

二、执行main方法

一个Java程序的入口是main方法。我们先从执行简单的main方法开始,找找成就感。在这个main方法里我们不创建对象,也不涉及到方法调用,类变量、实例变量等,因而只需要实现简单的指令即可。

1. 寻找main方法

首先我们要找到main方法,可以从我们解析出来的Class结果的methods数组中查找。

Class的结构(有省略):

typedef struct _ClassFile{
    uint magic;
    ...
    ushort constant_pool_count;
    cp_info constant_pool;
    ...
    ushort methods_count;
    method_info **methods;
    ...
} ClassFile;

typedef ClassFile Class;

查找main方法:

method_info* findMainMethod(Class *pclass)
{
    ushort index=0;
    while(index < pclass->methods_count) {
        if(IS_MAI
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值