通上面的学习,先初始化解释器的状态,然后调用解释器来执行方法。解释器又分为两种,一种是使用汇编写成性能优化的解释器,一种是使用标准C语言写成的解释器,可以很通用,理解起来也容易一些。接着下来,就先来理解C语言写成的解释器,函数dvmInterpretStd的代码如下:
boolINTERP_FUNC_NAME(Thread* self,InterpState* interpState)
{
这个函数输入两个参数,第一个参数self是执行代码的线程对象;第二个参数interpState是解释器运行相关参数。
#ifdefined(EASY_GDB)
StackSaveArea* debugSaveArea =SAVEAREA_FROM_FP(self->curFrame);
#endif
这段代码保存调试数据,方便查看。
#ifINTERP_TYPE == INTERP_DBG
bool debugIsMethodEntry =interpState->debugIsMethodEntry;
#endif
#ifdefined(WITH_TRACKREF_CHECKS)
intdebugTrackedRefStart = interpState->debugTrackedRefStart;
#endif
DvmDex*methodClassDex; //curMethod->clazz->pDvmDex
JValueretval;
/*core state */
constMethod*curMethod; // method we'reinterpreting
constu2* pc; // program counter
u4*fp; // frame pointer
u2inst; // currentinstruction
/*instruction decoding */
u2ref; // 16-bitquantity fetched directly
u2vsrc1, vsrc2, vdst; // usually usedfor register indexes
/*method call setup */
constMethod*methodToCall;
boolmethodCallRange;
这段代码是定义解释器后面使用到的变量。
#ifdefined(THREADED_INTERP)
/*static computed goto table */
DEFINE_GOTO_TABLE(handlerTable);
#endif
#ifdefined(WITH_JIT)
#if0
LOGD("*DebugInterp- entrypointis %d, tgtis 0x%x, %s\n",
interpState->entryPoint,
interpState->pc,
interpState->method->name);
#endif
#ifINTERP_TYPE == INTERP_DBG
/*Check to see if we've got a trace selection request. If we do,
* but something is amiss,revert to the fast interpreter.
*/
if(dvmJitCheckTraceRequest(self,interpState)) {
interpState->nextMode =INTERP_STD;
//LOGD("**something wrong, exiting\n");
returntrue;
}
#endif
#endif
/*copy state in */
curMethod =interpState->method;
pc = interpState->pc;
fp = interpState->fp;
retval = interpState->retval; /* only need for kInterpEntryReturn?*/
这段代码保存方法、指令指针pc、函数栈指针fp、返回指针。
methodClassDex =curMethod->clazz->pDvmDex;
这行代码是保存方法所在的Dex文件指针。
LOGVV("threadid=%d:entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
self->threadId,(interpState->nextMode == INTERP_STD) ? "STD": "DBG",
curMethod->clazz->descriptor,curMethod->name, pc - curMethod->insns,
fp,interpState->entryPoint);
/*
* DEBUG: scramble this toensure we're not relying on it.
*/
methodToCall = (constMethod*)-1;
#ifINTERP_TYPE == INTERP_DBG
if(debugIsMethodEntry) {
ILOGD("|--Now interpreting %s.%s",curMethod->clazz->descriptor,
curMethod->name);
DUMP_REGS(curMethod,interpState->fp, false);
}
#endif
switch(interpState->entryPoint) {
casekInterpEntryInstr:
/*just fall through to instruction loop or threaded kickstart */
break;
casekInterpEntryReturn:
gotoreturnFromMethod;
casekInterpEntryThrow:
gotoexceptionThrown;
default:
dvmAbort();
}
这段代码是根据不同的入口点进行处理。
#ifdefTHREADED_INTERP
FINISH(0); /* fetch and executefirst instruction */
#else
下面开始循环地解释每条指令,直到指令完成退出为止。
while(1) {
CHECK_DEBUG_AND_PROF(); /*service debugger and profiling */
CHECK_TRACKED_REFS(); /*check local reference tracking */
/*fetch the next 16 bits from the instruction stream */
inst = FETCH(0);
这行代码是从指令缓冲区里获取第一条指令,在Dalvik虚拟机里每条指令的大小是两个字节,不像JAVA虚拟机,只有一个字节。
switch(INST_INST(inst)) {
这行代码是根据指令选择不同的操作函数,实现每条指令实际需要操作内容,因此下面有很分支处理。
#endif
/*---start of opcodes ---*/
/*File: c/OP_NOP.c */
HANDLE_OPCODE(OP_NOP)
FINISH(1);
OP_END
这段代码,就是一个case语句,实现空指令OP_NOP的操作。空指令,就是什么事情都不做,只是简单地把执行指令的指针移到下一个指令。
/*File: c/OP_MOVE.c */
HANDLE_OPCODE(OP_MOVE/*vA, vB*/)
vdst = INST_A(inst);
vsrc1 = INST_B(inst);
ILOGV("|move%sv%d,v%d %s(v%d=0x%08x)",
(INST_INST(inst) ==OP_MOVE) ? "": "-object",vdst, vsrc1,
kSpacing, vdst,GET_REGISTER(vsrc1));
SET_REGISTER(vdst,GET_REGISTER(vsrc1));
FINISH(1);
OP_END
这段代码就是实现8位操作数的移动功能。后面还有很多指令,都是像这样的实现,就可以解释执行所有JAVA编写的应用程序了。画面漂亮、功能复杂的应用程序,就是通过这个解释器执行来实现这些功能的,到这里就把Dalvik虚拟机执行流程全部学习完成。