executeJava 主要在 /src/interp/engine/interp.c 和 interp-inlining.h 中。
解释器的实现,对于 JVM 提供的每一个指令,在 interp.c 中都有一个对应的解释代码。其基本形式如下。
每一条指令对应一个代码片断,执行一个 Java 方法的时候,就将这个 Java 方法转化成一个指令数组。然后直接使用 goto 关键字进行方法调用,直到所有的指令的执行完毕。这个 Java 方法也就执行完成了。
实现 将字节码转换成解释器片断的代码。 interp/direct.c prepare函数 用来实现将 MethodBlock 中的代码翻译成已经编译好的解释代码的入口地址。
src\interp\engine\interp-direct-common.h 中定义了
1 2 3 4 5 6 7 8 PREPARE_MB(mb); pc = (CodePntr)mb->code;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 typedef struct instruction { const void *handler; Operand operand; } Instruction; typedef union ins_operand { uintptr_t u; int i; struct { signed short i1; signed short i2; } ii; struct { unsigned short u1; unsigned short u2; } uu; struct { unsigned short u1; unsigned char u2; char i; } uui; void *pntr; } Operand;
执行完毕之后的 mb->code 存储的格式如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ++++++++++++ pc --> | handler | ++++++++++++ | handler | ++++++++++++ | handler | ++++++++++++ | handler | ++++++++++++ | handler | ++++++++++++ | handler | ++++++++++++ | ... | ++++++++++++ | handler | ++++++++++++ | return | ++++++++++++
handler
指向的是字节码对应的解释片断的代码入口。代码执行流程如下。
1 2 3 call pc->handler pc++; call pc->handler
直到 handler 是 return 指令时,方法调用结束。
###
gdb调试 1 2 3 4 p mb.code x/9xb mb.code
handlers 初始化 使用下面的使用可以将 interp.c 进行预编译
1 2 cd jamvmgcc -I . -E -P interp/engine/interp.c -o interp.i
Jamvm 解释器实现的基本思路是:
JVM 使用 1个字节来定义指令。所以 JVM 能够支持的指令的个数最多是 256 个。而一个 JVM 指令就相当于一个 函数功能。
所以就可以实现一个 JVM 指令和函数入口的对照表。然后所谓执行一个 JAVA 方法的字节码,其实就是执行上面指令所对应的函数。
所以 executeJava 方法的第一条语句:
1 2 3 INTERPRETER_DEFINITIONS
初始化指令和指令实现代码(这里使用的是标签,而不是函数,这样调用开销大大下降)的一个静态对照表。有了这个对照表,就可以实现进行下一步操作。
PREPARE_MB(mb) 的实现 其核心是 src/interp/direct.c 中实现
这个方法对 Methods 的 code 进行两遍循环。
其主要功能是将方法的 MethodBlock 中的 code 进行循环,将其转成,一个 Instruction *new_code 数组。并且将 MethodBlock 的状态设置成,已经解析,同时将 MethodBlock.code_size 设置成指令的个数,此后就可以直接调用这些方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 typedef union ins_operand { uintptr_t u; int i; struct { signed short i1; signed short i2; } ii; struct { unsigned short u1; unsigned short u2; } uu; struct { unsigned short u1; unsigned char u2; char i; } uui; void *pntr; } Operand; typedef struct instruction { const void *handler; Operand operand; } Instruction; int ins_count = 0 ; for (pass = 0 ; pass < 2 ; pass++) { int block_quickened = FALSE; #ifdef INLINING int block_start = 0 ; #endif int cache = 0 ; int pc; if (pass == 1 ) new_code = sysMalloc((ins_count + 1 ) * sizeof (Instruction)); for (ins_count = 0 , pc = 0 ; pc < code_len; ins_count++) { int quickened = FALSE; Operand operand; int ins_cache; int opcode; switch (opcode) { default :
参考
JamVM 栈顶缓存
获取标签地址
C99 goto label地址实现C语言协程
使用 goto + label 的例子
JamVM的解释器
Interpreter Implementation Choices