diff options
Diffstat (limited to 'vm/mterp/c/gotoTargets.cpp')
-rw-r--r-- | vm/mterp/c/gotoTargets.cpp | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/vm/mterp/c/gotoTargets.cpp b/vm/mterp/c/gotoTargets.cpp new file mode 100644 index 0000000..9d90046 --- /dev/null +++ b/vm/mterp/c/gotoTargets.cpp @@ -0,0 +1,1032 @@ +/* + * C footer. This has some common code shared by the various targets. + */ + +/* + * Everything from here on is a "goto target". In the basic interpreter + * we jump into these targets and then jump directly to the handler for + * next instruction. Here, these are subroutines that return to the caller. + */ + +GOTO_TARGET(filledNewArray, bool methodCallRange, bool jumboFormat) + { + ClassObject* arrayClass; + ArrayObject* newArray; + u4* contents; + char typeCh; + int i; + u4 arg5; + + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* class ref */ + vsrc1 = FETCH(3); /* #of elements */ + vdst = FETCH(4); /* range base */ + arg5 = -1; /* silence compiler warning */ + ILOGV("|filled-new-array/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + } else { + ref = FETCH(1); /* class ref */ + vdst = FETCH(2); /* first 4 regs -or- range base */ + + if (methodCallRange) { + vsrc1 = INST_AA(inst); /* #of elements */ + arg5 = -1; /* silence compiler warning */ + ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + } else { + arg5 = INST_A(inst); + vsrc1 = INST_B(inst); /* #of elements */ + ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}", + vsrc1, ref, vdst, arg5); + } + } + + /* + * Resolve the array class. + */ + arrayClass = dvmDexGetResolvedClass(methodClassDex, ref); + if (arrayClass == NULL) { + arrayClass = dvmResolveClass(curMethod->clazz, ref, false); + if (arrayClass == NULL) + GOTO_exceptionThrown(); + } + /* + if (!dvmIsArrayClass(arrayClass)) { + dvmThrowRuntimeException( + "filled-new-array needs array class"); + GOTO_exceptionThrown(); + } + */ + /* verifier guarantees this is an array class */ + assert(dvmIsArrayClass(arrayClass)); + assert(dvmIsClassInitialized(arrayClass)); + + /* + * Create an array of the specified type. + */ + LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor); + typeCh = arrayClass->descriptor[1]; + if (typeCh == 'D' || typeCh == 'J') { + /* category 2 primitives not allowed */ + dvmThrowRuntimeException("bad filled array req"); + GOTO_exceptionThrown(); + } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') { + /* TODO: requires multiple "fill in" loops with different widths */ + LOGE("non-int primitives not implemented"); + dvmThrowInternalError( + "filled-new-array not implemented for anything but 'int'"); + GOTO_exceptionThrown(); + } + + newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK); + if (newArray == NULL) + GOTO_exceptionThrown(); + + /* + * Fill in the elements. It's legal for vsrc1 to be zero. + */ + contents = (u4*)(void*)newArray->contents; + if (methodCallRange) { + for (i = 0; i < vsrc1; i++) + contents[i] = GET_REGISTER(vdst+i); + } else { + assert(vsrc1 <= 5); + if (vsrc1 == 5) { + contents[4] = GET_REGISTER(arg5); + vsrc1--; + } + for (i = 0; i < vsrc1; i++) { + contents[i] = GET_REGISTER(vdst & 0x0f); + vdst >>= 4; + } + } + if (typeCh == 'L' || typeCh == '[') { + dvmWriteBarrierArray(newArray, 0, newArray->length); + } + + retval.l = (Object*)newArray; + } + if (jumboFormat) { + FINISH(5); + } else { + FINISH(3); + } +GOTO_TARGET_END + + +GOTO_TARGET(invokeVirtual, bool methodCallRange, bool jumboFormat) + { + Method* baseMethod; + Object* thisPtr; + + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* method ref */ + vsrc1 = FETCH(3); /* count */ + vdst = FETCH(4); /* first reg */ + ADJUST_PC(2); /* advance pc partially to make returns easier */ + ILOGV("|invoke-virtual/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisPtr = (Object*) GET_REGISTER(vdst); + } else { + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* method ref */ + vdst = FETCH(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); + ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisPtr = (Object*) GET_REGISTER(vdst); + } else { + assert((vsrc1>>4) > 0); + ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisPtr = (Object*) GET_REGISTER(vdst & 0x0f); + } + } + + if (!checkForNull(thisPtr)) + GOTO_exceptionThrown(); + + /* + * 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) { + ILOGV("+ unknown method or access denied"); + 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]; + +#if defined(WITH_JIT) && defined(MTERP_STUB) + self->methodToCall = methodToCall; + self->callsiteClass = thisPtr->clazz; +#endif + +#if 0 + if (dvmIsAbstractMethod(methodToCall)) { + /* + * This can happen if you create two classes, Base and Sub, where + * Sub is a sub-class of Base. Declare a protected abstract + * method foo() in Base, and invoke foo() from a method in Base. + * Base is an "abstract base class" and is never instantiated + * directly. Now, Override foo() in Sub, and use Sub. This + * Works fine unless Sub stops providing an implementation of + * the method. + */ + dvmThrowAbstractMethodError("abstract method not implemented"); + GOTO_exceptionThrown(); + } +#else + assert(!dvmIsAbstractMethod(methodToCall) || + methodToCall->nativeFunc != NULL); +#endif + + LOGVV("+++ base=%s.%s virtual[%d]=%s.%s", + baseMethod->clazz->descriptor, baseMethod->name, + (u4) baseMethod->methodIndex, + methodToCall->clazz->descriptor, methodToCall->name); + assert(methodToCall != NULL); + +#if 0 + if (vsrc1 != methodToCall->insSize) { + LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s", + baseMethod->clazz->descriptor, baseMethod->name, + (u4) baseMethod->methodIndex, + methodToCall->clazz->descriptor, methodToCall->name); + //dvmDumpClass(baseMethod->clazz); + //dvmDumpClass(methodToCall->clazz); + dvmDumpAllClasses(0); + } +#endif + + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + +GOTO_TARGET(invokeSuper, bool methodCallRange, bool jumboFormat) + { + Method* baseMethod; + u2 thisReg; + + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* method ref */ + vsrc1 = FETCH(3); /* count */ + vdst = FETCH(4); /* first reg */ + ADJUST_PC(2); /* advance pc partially to make returns easier */ + ILOGV("|invoke-super/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisReg = vdst; + } else { + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* method ref */ + vdst = FETCH(2); /* 4 regs -or- first reg */ + + if (methodCallRange) { + ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisReg = vdst; + } else { + ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisReg = vdst & 0x0f; + } + } + + /* impossible in well-formed code, but we must check nevertheless */ + if (!checkForNull((Object*) GET_REGISTER(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) { + ILOGV("+ unknown method or access denied"); + 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]; + +#if 0 + if (dvmIsAbstractMethod(methodToCall)) { + dvmThrowAbstractMethodError("abstract method not implemented"); + GOTO_exceptionThrown(); + } +#else + assert(!dvmIsAbstractMethod(methodToCall) || + methodToCall->nativeFunc != NULL); +#endif + LOGVV("+++ base=%s.%s super-virtual=%s.%s", + baseMethod->clazz->descriptor, baseMethod->name, + methodToCall->clazz->descriptor, methodToCall->name); + assert(methodToCall != NULL); + + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + +GOTO_TARGET(invokeInterface, bool methodCallRange, bool jumboFormat) + { + Object* thisPtr; + ClassObject* thisClass; + + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* method ref */ + vsrc1 = FETCH(3); /* count */ + vdst = FETCH(4); /* first reg */ + ADJUST_PC(2); /* advance pc partially to make returns easier */ + ILOGV("|invoke-interface/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisPtr = (Object*) GET_REGISTER(vdst); + } else { + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* method ref */ + vdst = FETCH(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); + ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisPtr = (Object*) GET_REGISTER(vdst); + } else { + assert((vsrc1>>4) > 0); + ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisPtr = (Object*) GET_REGISTER(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 defined(WITH_JIT) && defined(MTERP_STUB) + self->callsiteClass = thisClass; + self->methodToCall = methodToCall; +#endif + if (methodToCall == NULL) { + assert(dvmCheckException(self)); + GOTO_exceptionThrown(); + } + + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + +GOTO_TARGET(invokeDirect, bool methodCallRange, bool jumboFormat) + { + u2 thisReg; + + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* method ref */ + vsrc1 = FETCH(3); /* count */ + vdst = FETCH(4); /* first reg */ + ADJUST_PC(2); /* advance pc partially to make returns easier */ + ILOGV("|invoke-direct/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisReg = vdst; + } else { + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* method ref */ + vdst = FETCH(2); /* 4 regs -or- first reg */ + + if (methodCallRange) { + ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisReg = vdst; + } else { + ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisReg = vdst & 0x0f; + } + } + + if (!checkForNull((Object*) GET_REGISTER(thisReg))) + GOTO_exceptionThrown(); + + methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref); + if (methodToCall == NULL) { + methodToCall = dvmResolveMethod(curMethod->clazz, ref, + METHOD_DIRECT); + if (methodToCall == NULL) { + ILOGV("+ unknown direct method"); // should be impossible + GOTO_exceptionThrown(); + } + } + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + +GOTO_TARGET(invokeStatic, bool methodCallRange, bool jumboFormat) + EXPORT_PC(); + + if (jumboFormat) { + ref = FETCH(1) | (u4)FETCH(2) << 16; /* method ref */ + vsrc1 = FETCH(3); /* count */ + vdst = FETCH(4); /* first reg */ + ADJUST_PC(2); /* advance pc partially to make returns easier */ + ILOGV("|invoke-static/jumbo args=%d @0x%08x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + } else { + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* method ref */ + vdst = FETCH(2); /* 4 regs -or- first reg */ + + if (methodCallRange) + ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + else + ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + } + + methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref); + if (methodToCall == NULL) { + methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC); + if (methodToCall == NULL) { + ILOGV("+ unknown method"); + GOTO_exceptionThrown(); + } + +#if defined(WITH_JIT) && defined(MTERP_STUB) + /* + * The JIT needs dvmDexGetResolvedMethod() to return non-null. + * Include the check if this code is being used as a stub + * called from the assembly interpreter. + */ + if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) && + (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) { + /* Class initialization is still ongoing */ + dvmJitEndTraceSelect(self,pc); + } +#endif + } + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); +GOTO_TARGET_END + +GOTO_TARGET(invokeVirtualQuick, bool methodCallRange, bool jumboFormat) + { + Object* thisPtr; + + EXPORT_PC(); + + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* vtable index */ + vdst = FETCH(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); + ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisPtr = (Object*) GET_REGISTER(vdst); + } else { + assert((vsrc1>>4) > 0); + ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisPtr = (Object*) GET_REGISTER(vdst & 0x0f); + } + + if (!checkForNull(thisPtr)) + GOTO_exceptionThrown(); + + + /* + * Combine the object we found with the vtable offset in the + * method. + */ + assert(ref < (unsigned int) thisPtr->clazz->vtableCount); + methodToCall = thisPtr->clazz->vtable[ref]; +#if defined(WITH_JIT) && defined(MTERP_STUB) + self->callsiteClass = thisPtr->clazz; + self->methodToCall = methodToCall; +#endif + +#if 0 + if (dvmIsAbstractMethod(methodToCall)) { + dvmThrowAbstractMethodError("abstract method not implemented"); + GOTO_exceptionThrown(); + } +#else + assert(!dvmIsAbstractMethod(methodToCall) || + methodToCall->nativeFunc != NULL); +#endif + + LOGVV("+++ virtual[%d]=%s.%s", + ref, methodToCall->clazz->descriptor, methodToCall->name); + assert(methodToCall != NULL); + + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + +GOTO_TARGET(invokeSuperQuick, bool methodCallRange, bool jumboFormat) + { + u2 thisReg; + + EXPORT_PC(); + + vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ + ref = FETCH(1); /* vtable index */ + vdst = FETCH(2); /* 4 regs -or- first reg */ + + if (methodCallRange) { + ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}", + vsrc1, ref, vdst, vdst+vsrc1-1); + thisReg = vdst; + } else { + ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}", + vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); + thisReg = vdst & 0x0f; + } + /* impossible in well-formed code, but we must check nevertheless */ + if (!checkForNull((Object*) GET_REGISTER(thisReg))) + GOTO_exceptionThrown(); + +#if 0 /* impossible in optimized + verified code */ + if (ref >= curMethod->clazz->super->vtableCount) { + dvmThrowNoSuchMethodError(NULL); + GOTO_exceptionThrown(); + } +#else + assert(ref < (unsigned int) curMethod->clazz->super->vtableCount); +#endif + + /* + * 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 the method's class' superclass. + */ + methodToCall = curMethod->clazz->super->vtable[ref]; + +#if 0 + if (dvmIsAbstractMethod(methodToCall)) { + dvmThrowAbstractMethodError("abstract method not implemented"); + GOTO_exceptionThrown(); + } +#else + assert(!dvmIsAbstractMethod(methodToCall) || + methodToCall->nativeFunc != NULL); +#endif + LOGVV("+++ super-virtual[%d]=%s.%s", + ref, methodToCall->clazz->descriptor, methodToCall->name); + assert(methodToCall != NULL); + GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); + } +GOTO_TARGET_END + + + /* + * General handling for return-void, return, and return-wide. Put the + * return value in "retval" before jumping here. + */ +GOTO_TARGET(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. + */ + PERIODIC_CHECKS(0); + + ILOGV("> retval=0x%llx (leaving %s.%s %s)", + retval.j, curMethod->clazz->descriptor, curMethod->name, + curMethod->shorty); + //DUMP_REGS(curMethod, fp); + + saveArea = SAVEAREA_FROM_FP(fp); + +#ifdef EASY_GDB + debugSaveArea = saveArea; +#endif + + /* 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) { + PC_FP_TO_SELF(); + dvmReportReturn(self); + } + + if (dvmIsBreakFrame(fp)) { + /* bail without popping the method frame from stack */ + LOGVV("+++ returned into break frame"); + GOTO_bail(); + } + + /* update thread FP, and reset local variables */ + self->interpSave.curFrame = fp; + curMethod = SAVEAREA_FROM_FP(fp)->method; + self->interpSave.method = curMethod; + //methodClass = curMethod->clazz; + methodClassDex = curMethod->clazz->pDvmDex; + pc = saveArea->savedPc; + ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor, + curMethod->name, curMethod->shorty); + + /* use FINISH on the caller's invoke instruction */ + //u2 invokeInstr = INST_INST(FETCH(0)); + if (true /*invokeInstr >= OP_INVOKE_VIRTUAL && + invokeInstr <= OP_INVOKE_INTERFACE*/) + { + FINISH(3); + } else { + //LOGE("Unknown invoke instr %02x at %d", + // invokeInstr, (int) (pc - curMethod->insns)); + assert(false); + } + } +GOTO_TARGET_END + + + /* + * Jump here when the code throws an exception. + * + * By the time we get here, the Throwable has been created and the stack + * trace has been saved off. + */ +GOTO_TARGET(exceptionThrown) + { + Object* exception; + int catchRelPc; + + PERIODIC_CHECKS(0); + + /* + * We save off the exception and clear the exception status. While + * processing the exception we might need to load some Throwable + * classes, and we don't want class loader exceptions to get + * confused with this one. + */ + assert(dvmCheckException(self)); + exception = dvmGetException(self); + dvmAddTrackedAlloc(exception, self); + dvmClearException(self); + + LOGV("Handling exception %s at %s:%d", + exception->clazz->descriptor, curMethod->name, + dvmLineNumFromPC(curMethod, pc - curMethod->insns)); + + /* + * Report the exception throw to any "subMode" watchers. + * + * TODO: if the exception was thrown by interpreted code, control + * fell through native, and then back to us, we will report the + * exception at the point of the throw and again here. We can avoid + * this by not reporting exceptions when we jump here directly from + * the native call code above, but then we won't report exceptions + * that were thrown *from* the JNI code (as opposed to *through* it). + * + * The correct solution is probably to ignore from-native exceptions + * here, and have the JNI exception code do the reporting to the + * debugger. + */ + if (self->interpBreak.ctl.subMode != 0) { + PC_FP_TO_SELF(); + dvmReportExceptionThrow(self, exception); + } + + /* + * We need to unroll to the catch block or the nearest "break" + * frame. + * + * A break frame could indicate that we have reached an intermediate + * native call, or have gone off the top of the stack and the thread + * needs to exit. Either way, we return from here, leaving the + * exception raised. + * + * If we do find a catch block, we want to transfer execution to + * that point. + * + * Note this can cause an exception while resolving classes in + * the "catch" blocks. + */ + catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns, + exception, false, (void**)(void*)&fp); + + /* + * Restore the stack bounds after an overflow. This isn't going to + * be correct in all circumstances, e.g. if JNI code devours the + * exception this won't happen until some other exception gets + * thrown. If the code keeps pushing the stack bounds we'll end + * up aborting the VM. + * + * Note we want to do this *after* the call to dvmFindCatchBlock, + * because that may need extra stack space to resolve exception + * classes (e.g. through a class loader). + * + * It's possible for the stack overflow handling to cause an + * exception (specifically, class resolution in a "catch" block + * during the call above), so we could see the thread's overflow + * flag raised but actually be running in a "nested" interpreter + * frame. We don't allow doubled-up StackOverflowErrors, so + * we can check for this by just looking at the exception type + * in the cleanup function. Also, we won't unroll past the SOE + * point because the more-recent exception will hit a break frame + * as it unrolls to here. + */ + if (self->stackOverflowed) + dvmCleanupStackOverflow(self, exception); + + if (catchRelPc < 0) { + /* falling through to JNI code or off the bottom of the stack */ +#if DVM_SHOW_EXCEPTION >= 2 + LOGD("Exception %s from %s:%d not caught locally", + exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod), + dvmLineNumFromPC(curMethod, pc - curMethod->insns)); +#endif + dvmSetException(self, exception); + dvmReleaseTrackedAlloc(exception, self); + GOTO_bail(); + } + +#if DVM_SHOW_EXCEPTION >= 3 + { + const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method; + LOGD("Exception %s thrown from %s:%d to %s:%d", + exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod), + dvmLineNumFromPC(curMethod, pc - curMethod->insns), + dvmGetMethodSourceFile(catchMethod), + dvmLineNumFromPC(catchMethod, catchRelPc)); + } +#endif + + /* + * Adjust local variables to match self->interpSave.curFrame and the + * updated PC. + */ + //fp = (u4*) self->interpSave.curFrame; + curMethod = SAVEAREA_FROM_FP(fp)->method; + self->interpSave.method = curMethod; + //methodClass = curMethod->clazz; + methodClassDex = curMethod->clazz->pDvmDex; + pc = curMethod->insns + catchRelPc; + ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor, + curMethod->name, curMethod->shorty); + DUMP_REGS(curMethod, fp, false); // show all regs + + /* + * Restore the exception if the handler wants it. + * + * The Dalvik spec mandates that, if an exception handler wants to + * do something with the exception, the first instruction executed + * must be "move-exception". We can pass the exception along + * through the thread struct, and let the move-exception instruction + * clear it for us. + * + * If the handler doesn't call move-exception, we don't want to + * finish here with an exception still pending. + */ + if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION) + dvmSetException(self, exception); + + dvmReleaseTrackedAlloc(exception, self); + FINISH(0); + } +GOTO_TARGET_END + + + + /* + * 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. + */ +GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall, + u2 count, u2 regs) + { + STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;); + + //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; + + /* + * Copy args. This may corrupt vsrc1/vdst. + */ + if (methodCallRange) { + // 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 = OUTS_FROM_FP(fp, vsrc1); + for (i = 0; i < vsrc1; i++) + outs[i] = GET_REGISTER(vdst+i); + } else { + u4 count = vsrc1 >> 4; + + assert(count <= curMethod->outsSize); + assert(count == methodToCall->insSize); + assert(count <= 5); + + outs = OUTS_FROM_FP(fp, count); +#if 0 + 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] = GET_REGISTER(vsrc1 & 0x0f); + case 4: + outs[3] = GET_REGISTER(vdst >> 12); + case 3: + outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8); + case 2: + outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4); + case 1: + outs[0] = GET_REGISTER(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; + + ILOGV("> %s%s.%s %s", + dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "", + methodToCall->clazz->descriptor, methodToCall->name, + methodToCall->shorty); + + newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize; + newSaveArea = SAVEAREA_FROM_FP(newFp); + + /* verify that we have enough space */ + if (true) { + u1* bottom; + bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4); + if (bottom < self->interpStackEnd) { + /* stack overflow */ + LOGV("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(); + } + //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p", + // fp, newFp, newSaveArea, bottom); + } + +#ifdef LOG_INSTR + if (methodToCall->registersSize > methodToCall->insSize) { + /* + * This makes valgrind quiet when we print registers that + * haven't been initialized. Turn it off when the debug + * messages are disabled -- we want valgrind to report any + * used-before-initialized issues. + */ + memset(newFp, 0xcc, + (methodToCall->registersSize - methodToCall->insSize) * 4); + } +#endif + +#ifdef EASY_GDB + newSaveArea->prevSave = SAVEAREA_FROM_FP(fp); +#endif + newSaveArea->prevFrame = fp; + newSaveArea->savedPc = pc; +#if defined(WITH_JIT) && defined(MTERP_STUB) + newSaveArea->returnAddr = 0; +#endif + 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. + */ + PC_TO_SELF(); + dvmReportInvoke(self, methodToCall); + } + + if (!dvmIsNativeMethod(methodToCall)) { + /* + * "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; + self->interpSave.curFrame = fp = newFp; +#ifdef EASY_GDB + debugSaveArea = SAVEAREA_FROM_FP(newFp); +#endif + self->debugIsMethodEntry = true; // profiling, debugging + ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor, + curMethod->name, curMethod->shorty); + DUMP_REGS(curMethod, fp, true); // show input args + FINISH(0); // 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; + + DUMP_REGS(methodToCall, newFp, true); // show input args + + if (self->interpBreak.ctl.subMode != 0) { + dvmReportPreNativeInvoke(methodToCall, self, fp); + } + + ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor, + methodToCall->name, methodToCall->shorty); + + /* + * 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, fp); + } + + /* pop frame off */ + dvmPopJniLocals(self, newSaveArea); + self->interpSave.curFrame = fp; + + /* + * 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)) { + LOGV("Exception thrown by/below native code"); + GOTO_exceptionThrown(); + } + + ILOGD("> retval=0x%llx (leaving native)", retval.j); + ILOGD("> (return from native %s.%s to %s.%s %s)", + methodToCall->clazz->descriptor, methodToCall->name, + curMethod->clazz->descriptor, curMethod->name, + curMethod->shorty); + + //u2 invokeInstr = INST_INST(FETCH(0)); + if (true /*invokeInstr >= OP_INVOKE_VIRTUAL && + invokeInstr <= OP_INVOKE_INTERFACE*/) + { + FINISH(3); + } else { + //LOGE("Unknown invoke instr %02x at %d", + // invokeInstr, (int) (pc - curMethod->insns)); + assert(false); + } + } + } + assert(false); // should not get here +GOTO_TARGET_END |