/* * 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