标签:dalvik android dvminterpretportable
参考源码版本:Android-4.4.4_r2
提示:大部分分析直接注释在代码内。
dvmInterpret
函数中调用了dvmInterpretPortable
函数对方法的字节码进行解释执行,dvmInterpret
在dalvik/vm/interp/Interp.cpp
文件中。
dvmInterpretPortable
函数在dalvik/vm/mterp/out/InterpC-portable.cpp
文件中。
使用gcc -E -P -C InterpC-portable.cpp > InterpC-portable_.cpp
命令,把这个函数中的宏全部展开,简单解释一下这一条 命令:
使用 -E 把函数中的宏全部展开也会导致代码的部分丢失,如:
#if defined(EASY_GDB) StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame); #endif
EASY_GDB
那么上面的那段代码就不会输出。
#if 0 if (self->debugIsMethodEntry) { ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor, curMethod->name); DUMP_REGS(curMethod, self->interpSave.curFrame, false); } #endif
dvmInterpretPortable函数在InterpC-portable.cpp
文件中,这个文件是通过gen-mterp.py脚本生成的,可以查看有关分析文章:
下面是dvmInterpretPortable
函数的部分代码:
/* File: portable/entry.cpp */ /* * Main interpreter loop. * * This was written with an ARM implementation in mind. */ void dvmInterpretPortable(Thread* self) { DvmDex* methodClassDex; // curMethod->clazz->pDvmDex JValue retval; /* core state */ const Method* curMethod; // 我们要解释的方法。 const u2* pc; // 程序计数器。 u4* fp; // 栈指针(frame pointer)。经过分析我认为fp保存了方法的信息和寄存器数组。 u2 inst; // 当前指令。 /* instruction decoding */ u4 ref; // 16 or 32-bit quantity fetched directly u2 vsrc1, vsrc2, vdst; // usually used for register indexes /* method call setup */ const Method* methodToCall; bool methodCallRange; /* static computed goto table */ // DEFINE_GOTO_TABLE 定义在 dalvik/libdex/DexOpcodes.h 中。 // handlerTable 保存的是处理 dalvik 指令的表,它是一个 "const void*" 类型的数组。 DEFINE_GOTO_TABLE (handlerTable); /* copy state in */ // curMethod将指向Method结构。 // 对 self->interpSave.method 赋值的代码可以在dvmInterpret函数中找到。 curMethod = self->interpSave.method; // 此时,pc指向的是方法指令的起始地址。 // self->interpSave.pc保存的地址来自于method->inst,可以在dvmInterpret函数中找到相关代码。 pc = self->interpSave.pc; fp = self->interpSave.curFrame; retval = self->interpSave.retval; /* only need for kInterpEntryReturn? */ // pc的值应该与curMethod->inst相等。 methodClassDex = curMethod->clazz->pDvmDex; LOGVV("threadid=%d: %s.%s pc=%#x fp=%p", self->threadId, curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns, fp); /* * Handle any ongoing profiling and prep for debugging. */ if (self->interpBreak.ctl.subMode != 0) { TRACE_METHOD_ENTER(self, curMethod); self->debugIsMethodEntry = true; // Always true on startup } /* * DEBUG: scramble this to ensure we're not relying on it. */ methodToCall = (const Method*) -1; // 抓取和执行第一条指令。 { do { pc += 0; ; } while (false); inst = (pc[(0)]); if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; }; /* fetch and execute first instruction */ // 经过分析下面的指令得出,代表指令的二进制保存在16位中的低8位。 /*--- start of opcodes ---*/ /* File: c/OP_NOP.cpp */ op_OP_NOP: { do { pc += 1; ; } while (false); inst = (pc[(0)]); if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; }; /* File: c/OP_MOVE.cpp */ op_OP_MOVE /*vA, vB*/: // vA、vB的索引在16位中的高8位。 vdst = (((inst) >> 8) & 0x0f); // vA vsrc1 = ((inst) >> 12); // vB ((void) 0); (fp[(vdst)] = ((fp[(vsrc1)]))); { do { pc += 1; ; } while (false); inst = (pc[(0)]); // 获得的是下一条指令。 if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; // 跳转到下一条指令继续解释执行。 }; // 这个指令占用32位,而pc是一个u2的数组。 // 所以下面的pc加2。 /* File: c/OP_MOVE_FROM16.cpp */ op_OP_MOVE_FROM16 /*vAA, vBBBB*/: // vA的索引在16位中的高8位 vdst = ((inst) >> 8); vsrc1 = (pc[(1)]); // vB 占用16位。 ((void) 0); (fp[(vdst)] = ((fp[(vsrc1)]))); { do { pc += 2; ; } while (false); inst = (pc[(0)]); // 获取下一条指令。 if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; // 跳转到下一条指令继续解释执行。 }; ...... /* File: c/OP_RETURN_VOID.cpp */ op_OP_RETURN_VOID /**/: ((void) 0); retval.j = 0xababababULL; // placate valgrind goto returnFromMethod;; /* File: c/OP_RETURN.cpp */ op_OP_RETURN /*vAA*/: vsrc1 = ((inst) >> 8); ((void) 0); retval.i = (fp[(vsrc1)]); // 获得返回值。 goto returnFromMethod;; /* File: c/OP_RETURN_WIDE.cpp */ op_OP_RETURN_WIDE /*vAA*/: vsrc1 = ((inst) >> 8); ((void) 0); retval.j = getLongFromArray(fp, (vsrc1)); // 获得返回值。 goto returnFromMethod;; /* File: c/OP_RETURN_OBJECT.cpp */ /* File: c/OP_RETURN.cpp */ op_OP_RETURN_OBJECT /*vAA*/: vsrc1 = ((inst) >> 8); ((void) 0); retval.i = (fp[(vsrc1)]); // 获得返回值。 goto returnFromMethod;; ...... /* File: c/OP_INVOKE_VIRTUAL.cpp */ op_OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/: do { methodCallRange = false; // 不是invoke-kind/range类的调用。 goto invokeVirtual; } while (false); /* File: c/OP_INVOKE_SUPER.cpp */ op_OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/: do { methodCallRange = false; goto invokeSuper; } while (false); /* File: c/OP_INVOKE_DIRECT.cpp */ op_OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/: do { methodCallRange = false; goto invokeDirect; } while (false); /* File: c/OP_INVOKE_STATIC.cpp */ op_OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/: do { methodCallRange = false; goto invokeStatic; } while (false); /* File: c/OP_INVOKE_INTERFACE.cpp */ op_OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/: do { methodCallRange = false; goto invokeInterface; } while (false); /* File: c/OP_UNUSED_73.cpp */ op_OP_UNUSED_73: /* File: c/OP_INVOKE_VIRTUAL_RANGE.cpp */ op_OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/: do { methodCallRange = true; // 是invoke-kind/range类的调用。 goto invokeVirtual; } while (false); /* File: c/OP_INVOKE_SUPER_RANGE.cpp */ op_OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/: do { methodCallRange = true; goto invokeSuper; } while (false); /* File: c/OP_INVOKE_DIRECT_RANGE.cpp */ op_OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/: do { methodCallRange = true; goto invokeDirect; } while (false); /* File: c/OP_INVOKE_STATIC_RANGE.cpp */ op_OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/: do { methodCallRange = true; goto invokeStatic; } while (false); /* File: c/OP_INVOKE_INTERFACE_RANGE.cpp */ op_OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/: do { methodCallRange = true; goto invokeInterface; } while (false); ...... invokeVirtual: { Method* baseMethod; Object* thisPtr; (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); // 保存的vsrc1是寄存器个数。 vsrc1 = ((inst) >> 8); /* AA (count) or BA (count + arg 5) */ ref = (pc[(1)]); /* method ref */ // 方法引用。 // 如果不是range调用,vdst代表的是4个寄存器,每个寄存器占4位, // 这也就解释了为什么invoke-kind指令中的寄存器索引不能大于16。 // 如果是range调用,那么vdst代表的就是第一个寄存器。 vdst = (pc[(2)]); /* 4 regs -or- first reg */ // 我们正在执行的方法的第一个参数总是这个方法所属的类对象。 /* * The object against which we are executing a method is always * in the first argument. */ if (methodCallRange) { assert(vsrc1 > 0); ((void) 0); thisPtr = (Object*) (fp[(vdst)]); // invoke-kind/range的this对象。 } else { assert((vsrc1 >> 4) > 0); ((void) 0); thisPtr = (Object*) (fp[(vdst & 0x0f)]); // invoke-kind的this对象。 } if (!checkForNull(thisPtr)) goto exceptionThrown;; // 解析方法。这是对象的静态类型中正确的方法。我们也在此验证访问权限。 // dvmDexGetResolvedMethod这个函数应该是获得类中的方法。 /* * Resolve the method. This is the correct method for the static * type of the object. We also verify access permissions here. */ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref); if (baseMethod == NULL) { baseMethod = dvmResolveMethod(curMethod->clazz, ref, METHOD_VIRTUAL); if (baseMethod == NULL) { ((void) 0); goto exceptionThrown;; } } // 结合对象中方法的虚函数表偏移。 /* * Combine the object we found with the vtable offset in the * method. */ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount); methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex]; assert( !dvmIsAbstractMethod(methodToCall) || methodToCall->nativeFunc != NULL); LOGVV("+++ base=%s.%s virtual[%d]=%s.%s", baseMethod->clazz->descriptor, baseMethod->name, (u4) baseMethod->methodIndex, methodToCall->clazz->descriptor, methodToCall->name); assert(methodToCall != NULL); goto invokeMethod;; } invokeSuper: { Method* baseMethod; u2 thisReg; (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); vsrc1 = ((inst) >> 8); /* AA (count) or BA (count + arg 5) */ ref = (pc[(1)]); /* method ref */ vdst = (pc[(2)]); /* 4 regs -or- first reg */ if (methodCallRange) { ((void) 0); thisReg = vdst; } else { ((void) 0); thisReg = vdst & 0x0f; } /* impossible in well-formed code, but we must check nevertheless */ if (!checkForNull((Object*) (fp[(thisReg)]))) goto exceptionThrown;; /* * Resolve the method. This is the correct method for the static * type of the object. We also verify access permissions here. * The first arg to dvmResolveMethod() is just the referring class * (used for class loaders and such), so we don't want to pass * the superclass into the resolution call. */ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref); if (baseMethod == NULL) { baseMethod = dvmResolveMethod(curMethod->clazz, ref, METHOD_VIRTUAL); if (baseMethod == NULL) { ((void) 0); goto exceptionThrown;; } } /* * Combine the object we found with the vtable offset in the * method's class. * * We're using the current method's class' superclass, not the * superclass of "this". This is because we might be executing * in a method inherited from a superclass, and we want to run * in that class' superclass. */ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) { /* * Method does not exist in the superclass. Could happen if * superclass gets updated. */ dvmThrowNoSuchMethodError(baseMethod->name); goto exceptionThrown;; } methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex]; assert( !dvmIsAbstractMethod(methodToCall) || methodToCall->nativeFunc != NULL); LOGVV("+++ base=%s.%s super-virtual=%s.%s", baseMethod->clazz->descriptor, baseMethod->name, methodToCall->clazz->descriptor, methodToCall->name); assert(methodToCall != NULL); goto invokeMethod;; } invokeInterface: { Object* thisPtr; ClassObject* thisClass; (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); vsrc1 = ((inst) >> 8); /* AA (count) or BA (count + arg 5) */ ref = (pc[(1)]); /* method ref */ vdst = (pc[(2)]); /* 4 regs -or- first reg */ /* * The object against which we are executing a method is always * in the first argument. */ if (methodCallRange) { assert(vsrc1 > 0); ((void) 0); thisPtr = (Object*) (fp[(vdst)]); } else { assert((vsrc1 >> 4) > 0); ((void) 0); thisPtr = (Object*) (fp[(vdst & 0x0f)]); } if (!checkForNull(thisPtr)) goto exceptionThrown;; thisClass = thisPtr->clazz; /* * Given a class and a method index, find the Method* with the * actual code we want to execute. */ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod, methodClassDex); if (methodToCall == NULL) { assert(dvmCheckException(self)); goto exceptionThrown;; } goto invokeMethod;; } invokeDirect: { u2 thisReg; (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); vsrc1 = ((inst) >> 8); /* AA (count) or BA (count + arg 5) */ ref = (pc[(1)]); /* method ref */ vdst = (pc[(2)]); /* 4 regs -or- first reg */ if (methodCallRange) { ((void) 0); thisReg = vdst; } else { ((void) 0); thisReg = vdst & 0x0f; } if (!checkForNull((Object*) (fp[(thisReg)]))) goto exceptionThrown;; methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref); if (methodToCall == NULL) { methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_DIRECT); if (methodToCall == NULL) { ((void) 0); // should be impossible goto exceptionThrown;; } } goto invokeMethod;; } invokeStatic: (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); vsrc1 = ((inst) >> 8); /* AA (count) or BA (count + arg 5) */ ref = (pc[(1)]); /* method ref */ vdst = (pc[(2)]); /* 4 regs -or- first reg */ if (methodCallRange) ((void) 0); else ((void) 0); methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref); if (methodToCall == NULL) { methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC); if (methodToCall == NULL) { ((void) 0); goto exceptionThrown;; } } goto invokeMethod;; ...... // 处理return-void、return和return-wide。 // 在跳转到这里之前要把返回值放到"retval"中。 /* * General handling for return-void, return, and return-wide. Put the * return value in "retval" before jumping here. */ returnFromMethod: { StackSaveArea* saveArea; /* * We must do this BEFORE we pop the previous stack frame off, so * that the GC can see the return value (if any) in the local vars. * * Since this is now an interpreter switch point, we must do it before * we do anything at all. */ { if (dvmCheckSuspendQuick(self)) { (((StackSaveArea*)(fp) -1)->xtra.currentPc = pc); dvmCheckSuspendPending(self); } }; ((void) 0); //DUMP_REGS(curMethod, fp); saveArea = ((StackSaveArea*)(fp) -1); // 恢复之前的fp。 /* back up to previous frame and see if we hit a break */ fp = (u4*) saveArea->prevFrame; assert(fp != NULL); /* Handle any special subMode requirements */ if (self->interpBreak.ctl.subMode != 0) { self->interpSave.pc = pc; self->interpSave.curFrame = fp; ; dvmReportReturn(self); } if (dvmIsBreakFrame(fp)) { /* bail without popping the method frame from stack */ LOGVV("+++ returned into break frame"); goto bail;; } // 更新fp并重置局部变量。 /* update thread FP, and reset local variables */ self->interpSave.curFrame = fp; curMethod = ((StackSaveArea*)(fp) -1)->method; self->interpSave.method = curMethod; //methodClass = curMethod->clazz; methodClassDex = curMethod->clazz->pDvmDex; pc = saveArea->savedPc; // 恢复pc。 ((void) 0); /* use FINISH on the caller's invoke instruction */ //u2 invokeInstr = INST_INST(FETCH(0)); if (true /*invokeInstr >= OP_INVOKE_VIRTUAL && invokeInstr <= OP_INVOKE_INTERFACE*/) { { do { // 跳过invoke指令,无论是invoke-kind还是invoke-kind/range指令都是占6个字节。 // pc += 3;正好跳过6个字节。 pc += 3; ; } while (false); inst = (pc[(0)]); // 取下一条指令, if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; // 继续执行指令。 }; } else { //ALOGE("Unknown invoke instr %02x at %d", // invokeInstr, (int) (pc - curMethod->insns)); assert(false); } } ...... /* * General handling for invoke-{virtual,super,direct,static,interface}, * including "quick" variants. * * Set "methodToCall" to the Method we're calling, and "methodCallRange" * depending on whether this is a "/range" instruction. * * For a range call: * "vsrc1" holds the argument count (8 bits) * "vdst" holds the first argument in the range * For a non-range call: * "vsrc1" holds the argument count (4 bits) and the 5th argument index * "vdst" holds four 4-bit register indices * * The caller must EXPORT_PC before jumping here, because any method * call can throw a stack overflow exception. */ invokeMethod: { ; //printf("range=%d call=%p count=%d regs=0x%04x\n", // methodCallRange, methodToCall, count, regs); //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor, // methodToCall->name, methodToCall->shorty); u4* outs; int i; // vsrc1的高4位保存了寄存器的个数。 // vsrc1的低4位也可以保存一个寄存器索引。 // vdst保存了寄存器索引或第一个寄存器的索引。 // 拷贝参数,这可能会破坏 vsrc1/vdst 。 /* * Copy args. This may corrupt vsrc1/vdst. */ if (methodCallRange) { // 可以使用 memcpy 或 一个 "Duff's device"; // could use memcpy or a "Duff's device"; most functions have // so few args it won't matter much assert(vsrc1 <= curMethod->outsSize); assert(vsrc1 == methodToCall->insSize); outs = ((u4*) ((u1*)((StackSaveArea*)(fp) -1) - sizeof(u4) * (vsrc1))); for (i = 0; i < vsrc1; i++) outs[i] = (fp[(vdst + i)]); } else { u4 count = vsrc1 >> 4; assert(count <= curMethod->outsSize); assert(count == methodToCall->insSize); assert(count <= 5); outs = ((u4*) ((u1*)((StackSaveArea*)(fp) -1) - sizeof(u4) * (count))); #if 0 // 这里的#if部分的代码是从源文件中拷贝出来的。 // 这里是循环赋值,但是却永远都不会执行,这是为什么哪? // else部分的代码才是不合理的代码啊。 if (count == 5) { outs[4] = GET_REGISTER(vsrc1 & 0x0f); count--; } for (i = 0; i < (int) count; i++) { outs[i] = GET_REGISTER(vdst & 0x0f); vdst >>= 4; } #else // This version executes fewer instructions but is larger // overall. Seems to be a teensy bit faster. assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear switch (count) { case 5: outs[4] = (fp[(vsrc1 & 0x0f)]); case 4: outs[3] = (fp[(vdst >> 12)]); case 3: outs[2] = (fp[((vdst & 0x0f00) >> 8)]); case 2: outs[1] = (fp[((vdst & 0x00f0) >> 4)]); case 1: outs[0] = (fp[(vdst & 0x0f)]); default: ; } #endif } } /* * (This was originally a "goto" target; I've kept it separate from the * stuff above in case we want to refactor things again.) * * At this point, we have the arguments stored in the "outs" area of * the current method's stack frame, and the method to call in * "methodToCall". Push a new stack frame. */ { StackSaveArea* newSaveArea; u4* newFp; ((void) 0); // 新的栈帧。 newFp = (u4*) ((StackSaveArea*)(fp) -1) - methodToCall->registersSize; newSaveArea = ((StackSaveArea*)(newFp) -1); // 验证我们是否有足够的空间。 /* verify that we have enough space */ if (true) { u1* bottom; bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4); if (bottom < self->interpStackEnd) { // 溢出。 /* stack overflow */ ALOGV( "Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')", self->interpStackStart, self->interpStackEnd, bottom, (u1*) fp - bottom, self->interpStackSize, methodToCall->name); dvmHandleStackOverflow(self, methodToCall); assert(dvmCheckException(self)); goto exceptionThrown;; } //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p", // fp, newFp, newSaveArea, bottom); } newSaveArea->prevFrame = fp; newSaveArea->savedPc = pc; newSaveArea->method = methodToCall; if (self->interpBreak.ctl.subMode != 0) { /* * We mark ENTER here for both native and non-native * calls. For native calls, we'll mark EXIT on return. * For non-native calls, EXIT is marked in the RETURN op. */ self->interpSave.pc = pc; ; dvmReportInvoke(self, methodToCall); } if (!dvmIsNativeMethod(methodToCall)) { // 调用解释的代码。复位 PC,更新栈帧和其他本地状态,并继续。 // 在这里切换了方法的上下文。 /* * "Call" interpreted code. Reposition the PC, update the * frame pointer and other local state, and continue. */ curMethod = methodToCall; self->interpSave.method = curMethod; methodClassDex = curMethod->clazz->pDvmDex; pc = methodToCall->insns; fp = newFp; self->interpSave.curFrame = fp; self->debugIsMethodEntry = true; // profiling, debugging ((void) 0); ((void) 0); // show input args { do { pc += 0; ; } while (false); inst = (pc[(0)]); if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; }; // jump to method start } else { /* set this up for JNI locals, even if not a JNI native */ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; self->interpSave.curFrame = newFp; ((void) 0); // show input args if (self->interpBreak.ctl.subMode != 0) { dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame); } ((void) 0); /* * Jump through native call bridge. Because we leave no * space for locals on native calls, "newFp" points directly * to the method arguments. */ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self); if (self->interpBreak.ctl.subMode != 0) { dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame); } /* pop frame off */ dvmPopJniLocals(self, newSaveArea); self->interpSave.curFrame = newSaveArea->prevFrame; fp = newSaveArea->prevFrame; /* * If the native code threw an exception, or interpreted code * invoked by the native call threw one and nobody has cleared * it, jump to our local exception handling. */ if (dvmCheckException(self)) { ALOGV("Exception thrown by/below native code"); goto exceptionThrown;; } ((void) 0); ((void) 0); //u2 invokeInstr = INST_INST(FETCH(0)); if (true /*invokeInstr >= OP_INVOKE_VIRTUAL && invokeInstr <= OP_INVOKE_INTERFACE*/) { { do { pc += 3; ; } while (false); inst = (pc[(0)]); if (self->interpBreak.ctl.subMode) { dvmCheckBefore(pc, fp, self); } goto *handlerTable[((inst) & 0xff)]; }; } else { //ALOGE("Unknown invoke instr %02x at %d", // invokeInstr, (int) (pc - curMethod->insns)); assert(false); } } } assert(false); // should not get here /* File: portable/enddefs.cpp */ /*--- end of opcodes ---*/ bail: ((void) 0); // note "curMethod" may be NULL self->interpSave.retval = retval; }
标签:dalvik android dvminterpretportable
原文地址:http://blog.csdn.net/zylc369/article/details/44870595