diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-20 14:03:55 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-20 14:03:55 -0800 |
| commit | 4c1a2915e40eceeb68dbc323d28b8bf8763af83b (patch) | |
| tree | b6df7a910dfb981a44919eb33fc29aaaca02b7b2 /vm/analysis | |
| parent | 066bed5fb19102e4f560f242b0cee645f1ed8b31 (diff) | |
| download | android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.tar.gz android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.tar.bz2 android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.zip | |
auto import from //branches/cupcake/...@127101
Diffstat (limited to 'vm/analysis')
| -rw-r--r-- | vm/analysis/CodeVerify.c | 184 | ||||
| -rw-r--r-- | vm/analysis/DexOptimize.c | 2 | ||||
| -rw-r--r-- | vm/analysis/DexVerify.c | 25 | ||||
| -rw-r--r-- | vm/analysis/RegisterMap.c | 695 | ||||
| -rw-r--r-- | vm/analysis/RegisterMap.h | 10 | ||||
| -rw-r--r-- | vm/analysis/VerifySubs.c | 38 | ||||
| -rw-r--r-- | vm/analysis/VerifySubs.h | 3 |
7 files changed, 876 insertions, 81 deletions
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c index 0b8a94d98..2c5e74945 100644 --- a/vm/analysis/CodeVerify.c +++ b/vm/analysis/CodeVerify.c @@ -327,32 +327,6 @@ static bool isTypeWidthEqual1nr(RegType type1, RegType type2) } /* - * Given a 32-bit constant, return the most-restricted RegType that can hold - * the value. - */ -static RegType determineCat1Const(s4 value) -{ - if (value < -32768) - return kRegTypeInteger; - else if (value < -128) - return kRegTypeShort; - else if (value < 0) - return kRegTypeByte; - else if (value == 0) - return kRegTypeZero; - else if (value == 1) - return kRegTypeOne; - else if (value < 128) - return kRegTypePosByte; - else if (value < 32768) - return kRegTypePosShort; - else if (value < 65536) - return kRegTypeChar; - else - return kRegTypeInteger; -} - -/* * Convert a VM PrimitiveType enum value to the equivalent RegType value. */ static RegType primitiveTypeToRegType(PrimitiveType primType) @@ -923,7 +897,6 @@ bad_sig: static RegType getMethodReturnType(const Method* meth) { RegType type; - bool okay = true; const char* descriptor = dexProtoGetReturnType(&meth->prototype); switch (*descriptor) { @@ -957,6 +930,7 @@ static RegType getMethodReturnType(const Method* meth) case 'L': case '[': { + bool okay = true; ClassObject* clazz = lookupClassByDescriptor(meth, descriptor, &okay); assert(okay); @@ -2455,7 +2429,7 @@ static void checkFinalFieldAccess(const Method* meth, const Field* field, /* make sure we're in the same class */ if (meth->clazz != field->clazz) { - LOG_VFY_METH(meth, "VFY: can't modify final field %s.%\n", + LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s\n", field->clazz->descriptor, field->name); *pOkay = false; return; @@ -2716,6 +2690,54 @@ static bool initRegisterTable(const Method* meth, const InsnFlags* insnFlags, /* + * Verify that the arguments in a filled-new-array instruction are valid. + * + * "resClass" is the class refered to by pDecInsn->vB. + */ +static void verifyFilledNewArrayRegs(const Method* meth, + const RegType* insnRegs, const int insnRegCount, + const DecodedInstruction* pDecInsn, ClassObject* resClass, bool isRange, + bool* pOkay) +{ + u4 argCount = pDecInsn->vA; + RegType expectedType; + PrimitiveType elemType; + unsigned int ui; + + assert(dvmIsArrayClass(resClass)); + elemType = resClass->elementClass->primitiveType; + if (elemType == PRIM_NOT) { + LOG_VFY("VFY: filled-new-array not yet supported on reference types\n"); + *pOkay = false; + return; + } + + expectedType = primitiveTypeToRegType(elemType); + //LOGI("filled-new-array: %s -> %d\n", resClass->descriptor, expectedType); + + /* + * Verify each register. If "argCount" is bad, verifyRegisterType() + * will run off the end of the list and fail. It's legal, if silly, + * for argCount to be zero. + */ + for (ui = 0; ui < argCount; ui++) { + u4 getReg; + + if (isRange) + getReg = pDecInsn->vC + ui; + else + getReg = pDecInsn->arg[ui]; + + verifyRegisterType(insnRegs, insnRegCount, getReg, expectedType, pOkay); + if (!*pOkay) { + LOG_VFY("VFY: filled-new-array arg %u(%u) not valid\n", ui, getReg); + return; + } + } +} + + +/* * =========================================================================== * Entry point and driver loop * =========================================================================== @@ -3309,12 +3331,12 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_CONST: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - determineCat1Const((s4)decInsn.vB), &okay); + dvmDetermineCat1Const((s4)decInsn.vB), &okay); break; case OP_CONST_HIGH16: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - determineCat1Const((s4) decInsn.vB << 16), &okay); + dvmDetermineCat1Const((s4) decInsn.vB << 16), &okay); break; case OP_CONST_WIDE_16: case OP_CONST_WIDE_32: @@ -3332,8 +3354,18 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, break; case OP_CONST_CLASS: assert(gDvm.classJavaLangClass != NULL); - setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(gDvm.classJavaLangClass), &okay); + /* make sure we can resolve the class; access check is important */ + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + if (resClass == NULL) { + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); + dvmLogUnableToResolveClass(badClassDesc, meth); + LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s\n", + decInsn.vB, badClassDesc, meth->clazz->descriptor); + okay = false; + } else { + setRegisterType(workRegs, insnRegCount, decInsn.vA, + regTypeFromClass(gDvm.classJavaLangClass), &okay); + } break; case OP_MONITOR_ENTER: @@ -3378,17 +3410,29 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, } break; case OP_INSTANCE_OF: + /* make sure we're checking a reference type */ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &okay); if (!okay) break; if (!regTypeIsReference(tmpType)) { - LOG_VFY("VFY: vB not a reference\n"); + LOG_VFY("VFY: vB not a reference (%d)\n", tmpType); okay = false; break; } - /* result is boolean */ - setRegisterType(workRegs, insnRegCount, decInsn.vA, - kRegTypeBoolean, &okay); + + /* make sure we can resolve the class; access check is important */ + resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); + if (resClass == NULL) { + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); + dvmLogUnableToResolveClass(badClassDesc, meth); + LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s\n", + decInsn.vC, badClassDesc, meth->clazz->descriptor); + okay = false; + } else { + /* result is boolean */ + setRegisterType(workRegs, insnRegCount, decInsn.vA, + kRegTypeBoolean, &okay); + } break; case OP_ARRAY_LENGTH: @@ -3442,7 +3486,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_NEW_ARRAY: resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); if (resClass == NULL) { - const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s\n", decInsn.vC, badClassDesc, meth->clazz->descriptor); @@ -3451,6 +3495,9 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, LOG_VFY("VFY: new-array on non-array class\n"); okay = false; } else { + /* make sure "size" register is valid type */ + verifyRegisterType(workRegs, insnRegCount, decInsn.vB, + kRegTypeInteger, &okay); /* set register type to array class */ setRegisterType(workRegs, insnRegCount, decInsn.vA, regTypeFromClass(resClass), &okay); @@ -3458,7 +3505,6 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, break; case OP_FILLED_NEW_ARRAY: case OP_FILLED_NEW_ARRAY_RANGE: - /* (decInsn.vA == 0) is silly, but not illegal */ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); @@ -3470,13 +3516,11 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, LOG_VFY("VFY: filled-new-array on non-array class\n"); okay = false; } else { - /* - * TODO: verify decInsn.vA range - * TODO: if resClass is array of references, verify the registers - * in the argument list against the array type. - * TODO: if resClass is array of primitives, verify that the - * contents of the registers are appropriate. - */ + bool isRange = (decInsn.opCode == OP_FILLED_NEW_ARRAY_RANGE); + + /* check the arguments to the instruction */ + verifyFilledNewArrayRegs(meth, workRegs, insnRegCount, &decInsn, + resClass, isRange, &okay); /* filled-array result goes into "result" register */ setResultRegisterType(workRegs, insnRegCount, regTypeFromClass(resClass), &okay); @@ -3802,6 +3846,7 @@ aget_1nr_common: if (!okay) break; + /* get the class of the array we're pulling an object from */ resClass = getClassFromRegister(workRegs, insnRegCount, decInsn.vB, &okay); if (!okay) @@ -3811,7 +3856,7 @@ aget_1nr_common: assert(resClass != NULL); if (!dvmIsArrayClass(resClass)) { - LOG_VFY("VFY: aget-object on non-ref array class\n"); + LOG_VFY("VFY: aget-object on non-array class\n"); okay = false; break; } @@ -3826,9 +3871,14 @@ aget_1nr_common: assert(resClass->arrayDim > 1); elementClass = dvmFindArrayClass(&resClass->descriptor[1], resClass->classLoader); - } else { + } else if (resClass->descriptor[1] == 'L') { assert(resClass->arrayDim == 1); elementClass = resClass->elementClass; + } else { + LOG_VFY("VFY: aget-object on non-ref array class (%s)\n", + resClass->descriptor); + okay = false; + break; } dstType = regTypeFromClass(elementClass); @@ -4655,7 +4705,7 @@ sput_1nr_common: case OP_INVOKE_INTERFACE: case OP_INVOKE_INTERFACE_RANGE: { - RegType thisType, returnType; + RegType /*thisType,*/ returnType; Method* absMethod; bool isRange; @@ -4666,6 +4716,7 @@ sput_1nr_common: if (!okay) break; +#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */ /* * Get the type of the "this" arg, which should always be an * interface class. Because we don't do a full merge on @@ -4676,7 +4727,6 @@ sput_1nr_common: if (!okay) break; -#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */ if (thisType == kRegTypeZero) { /* null pointer always passes (and always fails at runtime) */ } else { @@ -4934,10 +4984,40 @@ sput_1nr_common: break; + /* + * Verifying "quickened" instructions is tricky, because we have + * discarded the original field/method information. The byte offsets + * and vtable indices only have meaning in the context of an object + * instance. + * + * If a piece of code declares a local reference variable, assigns + * null to it, and then issues a virtual method call on it, we + * cannot evaluate the method call during verification. This situation + * isn't hard to handle, since we know the call will always result in an + * NPE, and the arguments and return value don't matter. Any code that + * depends on the result of the method call is inaccessible, so the + * fact that we can't fully verify anything that comes after the bad + * call is not a problem. + * + * We must also consider the case of multiple code paths, only some of + * which involve a null reference. We can completely verify the method + * if we sidestep the results of executing with a null reference. + * For example, if on the first pass through the code we try to do a + * virtual method invocation through a null ref, we have to skip the + * method checks and have the method return a "wildcard" type (which + * merges with anything to become that other thing). The move-result + * will tell us if it's a reference, single-word numeric, or double-word + * value. We continue to perform the verification, and at the end of + * the function any invocations that were never fully exercised are + * marked as null-only. + * + * We would do something similar for the field accesses. The field's + * type, once known, can be used to recover the width of short integers. + * If the object reference was null, the field-get returns the "wildcard" + * type, which is acceptable for any operation. + */ case OP_EXECUTE_INLINE: case OP_INVOKE_DIRECT_EMPTY: - okay = false; // TODO - implement optimized opcodes - break; case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: @@ -4948,7 +5028,7 @@ sput_1nr_common: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: - okay = false; // TODO - implement optimized opcodes + okay = false; break; /* these should never appear */ diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c index 90e4c6feb..09199dbab 100644 --- a/vm/analysis/DexOptimize.c +++ b/vm/analysis/DexOptimize.c @@ -1657,7 +1657,7 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) bool allowed = dvmCheckClassAccess(referrer, resClass); untweakLoader(referrer, resClass); if (!allowed) { - LOGI("DexOpt: resolve class illegal access: %s -> %s\n", + LOGW("DexOpt: resolve class illegal access: %s -> %s\n", referrer->descriptor, resClass->descriptor); return NULL; } diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c index f78133bb3..5a3e8bdad 100644 --- a/vm/analysis/DexVerify.c +++ b/vm/analysis/DexVerify.c @@ -331,13 +331,20 @@ static void decodeInstruction(const Method* meth, int insnIdx, */ static bool checkNewInstance(const Method* meth, int insnIdx) { - DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + DvmDex* pDvmDex = meth->clazz->pDvmDex; DecodedInstruction decInsn; const char* classDescriptor; + u4 idx; decodeInstruction(meth, insnIdx, &decInsn); - classDescriptor = dexStringByTypeIdx(pDexFile, decInsn.vB); // 2nd item + idx = decInsn.vB; // 2nd item + if (idx >= pDvmDex->pHeader->typeIdsSize) { + LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", + idx, pDvmDex->pHeader->typeIdsSize); + return false; + } + classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); if (classDescriptor[0] != 'L') { LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n", classDescriptor); @@ -354,12 +361,20 @@ static bool checkNewInstance(const Method* meth, int insnIdx) */ static bool checkNewArray(const Method* meth, int insnIdx) { - DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + DvmDex* pDvmDex = meth->clazz->pDvmDex; DecodedInstruction decInsn; const char* classDescriptor; + u4 idx; decodeInstruction(meth, insnIdx, &decInsn); - classDescriptor = dexStringByTypeIdx(pDexFile, decInsn.vC); // 3rd item + idx = decInsn.vC; // 3rd item + if (idx >= pDvmDex->pHeader->typeIdsSize) { + LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", + idx, pDvmDex->pHeader->typeIdsSize); + return false; + } + + classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); int bracketCount = 0; const char* cp = classDescriptor; @@ -589,7 +604,7 @@ static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, break; case OP_FILLED_NEW_ARRAY: - if (!checkTypeIndex(meth, i, false)) + if (!checkTypeIndex(meth, i, true)) return false; break; case OP_FILLED_NEW_ARRAY_RANGE: diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c index 8e0054ebf..eb243af2a 100644 --- a/vm/analysis/RegisterMap.c +++ b/vm/analysis/RegisterMap.c @@ -14,11 +14,13 @@ * limitations under the License. */ +// ** UNDER CONSTRUCTION ** + /* * This code generate "register maps" for Dalvik bytecode. In a stack-based - * VM we would call these "stack maps". They are used to increase the - * precision in the garbage collector when finding references in the - * interpreter call stack. + * VM we might call these "stack maps". They are used to increase the + * precision in the garbage collector when scanning references in the + * interpreter thread stacks. */ #include "Dalvik.h" #include "analysis/CodeVerify.h" @@ -36,6 +38,11 @@ results in the optimized DEX file, which increases their size by about 25% unless we use compression, and for performance reasons we don't want to just re-run the verifier. +On the plus side, we know that verification has completed successfully -- +or at least are allowed to assume that it would -- so we skip a lot of +the checks (like verifying that the register indices in instructions +are reasonable). + Both type-precise and live-precise information can be generated knowing only whether or not a register holds a reference. We don't need to know what kind of reference or whether the object has been initialized. @@ -64,7 +71,7 @@ typedef u1 SRegType; * can be category 1 or 2, so we need two slots. */ #define kExtraRegs 2 -#define RESULT_REGISTER(_insnRegCount) (_insnRegCount) +#define RESULT_REGISTER(_insnRegCountPlus) (_insnRegCountPlus - kExtraRegs) /* * Working state. @@ -84,7 +91,7 @@ typedef struct WorkState { * Number of registers we track for each instruction. This is equal * to the method's declared "registersSize" plus kExtraRegs. */ - int insnRegCount; + int insnRegCountPlus; /* * Instruction widths and flags, one entry per code unit. @@ -197,7 +204,7 @@ RegisterMap* dvmGenerateRegisterMap(const Method* meth) pState->method = meth; pState->insnsSize = dvmGetMethodInsnsSize(meth); - pState->insnRegCount = meth->registersSize + kExtraRegs; + pState->insnRegCountPlus = meth->registersSize + kExtraRegs; pState->insnFlags = calloc(sizeof(InsnFlags), pState->insnsSize); pState->addrRegs = calloc(sizeof(SRegType*), pState->insnsSize); @@ -228,7 +235,7 @@ RegisterMap* dvmGenerateRegisterMap(const Method* meth) for (offset = 0; offset < pState->insnsSize; offset++) { if (dvmInsnIsGcPoint(pState->insnFlags, offset)) { pState->addrRegs[offset] = regPtr; - regPtr += pState->insnRegCount; + regPtr += pState->insnRegCountPlus; } } assert(regPtr - pState->regAlloc == pState->insnsSize * gcPointCount); @@ -313,7 +320,7 @@ static bool setTypesFromSignature(WorkState* pState) while (*ccp != 0) { switch (*ccp) { case 'L': - case '[': + //case '[': *pCurReg++ = kRegTypeReference; break; case 'Z': @@ -386,7 +393,7 @@ static inline int compareRegisters(const SRegType* src1, const SRegType* src2, static bool analyzeMethod(WorkState* pState) { const Method* meth = pState->method; - SRegType workRegs[pState->insnRegCount]; + SRegType workRegs[pState->insnRegCountPlus]; InsnFlags* insnFlags = pState->insnFlags; int insnsSize = pState->insnsSize; int insnIdx, startGuess; @@ -454,7 +461,7 @@ static bool analyzeMethod(WorkState* pState) if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) { SRegType* insnRegs = getRegisterLine(pState, insnIdx); assert(insnRegs != NULL); - copyRegisters(workRegs, insnRegs, pState->insnRegCount); + copyRegisters(workRegs, insnRegs, pState->insnRegCountPlus); } else { #ifndef NDEBUG @@ -464,7 +471,8 @@ static bool analyzeMethod(WorkState* pState) */ SRegType* insnRegs = getRegisterLine(pState, insnIdx); if (insnRegs != NULL && - compareRegisters(workRegs, insnRegs, pState->insnRegCount) != 0) + compareRegisters(workRegs, insnRegs, + pState->insnRegCountPlus) != 0) { char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); LOG_VFY("HUH? workRegs diverged in %s.%s %s\n", @@ -494,6 +502,129 @@ bail: } /* + * Get a pointer to the method being invoked. + * + * Returns NULL on failure. + */ +static Method* getInvokedMethod(const Method* meth, + const DecodedInstruction* pDecInsn, MethodType methodType) +{ + Method* resMethod; + char* sigOriginal = NULL; + + /* + * Resolve the method. This could be an abstract or concrete method + * depending on what sort of call we're making. + */ + if (methodType == METHOD_INTERFACE) { + resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB); + } else { + resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType); + } + if (resMethod == NULL) { + /* failed; print a meaningful failure message */ + DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + const DexMethodId* pMethodId; + const char* methodName; + char* methodDesc; + const char* classDescriptor; + + pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB); + methodName = dexStringById(pDexFile, pMethodId->nameIdx); + methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId); + classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); + + LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n", + dvmMethodTypeStr(methodType), pDecInsn->vB, + classDescriptor, methodName, methodDesc); + free(methodDesc); + return NULL; + } + + return resMethod; +} + +/* + * Return the register type for the method. Since we don't care about + * the actual type, we can just look at the "shorty" signature. + * + * Returns kRegTypeUnknown for "void". + */ +static SRegType getMethodReturnType(const Method* meth) +{ + SRegType type; + + switch (meth->shorty[0]) { + case 'I': + type = kRegTypeInteger; + break; + case 'C': + type = kRegTypeChar; + break; + case 'S': + type = kRegTypeShort; + break; + case 'B': + type = kRegTypeByte; + break; + case 'Z': + type = kRegTypeBoolean; + break; + case 'V': + type = kRegTypeUnknown; + break; + case 'F': + type = kRegTypeFloat; + break; + case 'D': + type = kRegTypeDoubleLo; + break; + case 'J': + type = kRegTypeLongLo; + break; + case 'L': + //case '[': + type = kRegTypeReference; + break; + default: + /* we verified signature return type earlier, so this is impossible */ + assert(false); + type = kRegTypeConflict; + break; + } + + return type; +} + +/* + * Copy a category 1 register. + */ +static inline void copyRegister1(SRegType* insnRegs, u4 vdst, u4 vsrc) +{ + insnRegs[vdst] = insnRegs[vsrc]; +} + +/* + * Copy a category 2 register. Note the source and destination may overlap. + */ +static inline void copyRegister2(SRegType* insnRegs, u4 vdst, u4 vsrc) +{ + //memmove(&insnRegs[vdst], &insnRegs[vsrc], sizeof(SRegType) * 2); + SRegType r1 = insnRegs[vsrc]; + SRegType r2 = insnRegs[vsrc+1]; + insnRegs[vdst] = r1; + insnRegs[vdst+1] = r2; +} + +/* + * Set the type of a category 1 register. + */ +static inline void setRegisterType(SRegType* insnRegs, u4 vdst, SRegType type) +{ + insnRegs[vdst] = type; +} + +/* * Decode the specified instruction and update the register info. */ static bool handleInstruction(WorkState* pState, SRegType* workRegs, @@ -524,14 +655,15 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, * The behavior can be determined from the InstrFlags. */ DecodedInstruction decInsn; - SRegType entryRegs[pState->insnRegCount]; + SRegType entryRegs[pState->insnRegCountPlus]; + const int insnRegCountPlus = pState->insnRegCountPlus; bool justSetResult = false; int branchTarget = 0; + SRegType tmpType; dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn); const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode); - /* * Make a copy of the previous register state. If the instruction * throws an exception, we merge *this* into the destination rather @@ -541,14 +673,537 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, */ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx)) { - copyRegisters(entryRegs, workRegs, pState->insnRegCount); + copyRegisters(entryRegs, workRegs, insnRegCountPlus); } switch (decInsn.opCode) { case OP_NOP: break; - default: break; // TODO: fill this in + case OP_MOVE: + case OP_MOVE_FROM16: + case OP_MOVE_16: + case OP_MOVE_OBJECT: + case OP_MOVE_OBJECT_FROM16: + case OP_MOVE_OBJECT_16: + copyRegister1(workRegs, decInsn.vA, decInsn.vB); + break; + case OP_MOVE_WIDE: + case OP_MOVE_WIDE_FROM16: + case OP_MOVE_WIDE_16: + copyRegister2(workRegs, decInsn.vA, decInsn.vB); + break; + + /* + * The move-result instructions copy data out of a "pseudo-register" + * with the results from the last method invocation. In practice we + * might want to hold the result in an actual CPU register, so the + * Dalvik spec requires that these only appear immediately after an + * invoke or filled-new-array. + * + * These calls invalidate the "result" register. (This is now + * redundant with the reset done below, but it can make the debug info + * easier to read in some cases.) + */ + case OP_MOVE_RESULT: + case OP_MOVE_RESULT_OBJECT: + copyRegister1(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus)); + break; + case OP_MOVE_RESULT_WIDE: + copyRegister2(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus)); + break; + + case OP_MOVE_EXCEPTION: + /* + * This statement can only appear as the first instruction in an + * exception handler (though not all exception handlers need to + * have one of these). We verify that as part of extracting the + * exception type from the catch block list. + */ + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + break; + + case OP_CONST_4: + case OP_CONST_16: + case OP_CONST: + /* could be boolean, int, float, or a null reference */ + setRegisterType(workRegs, decInsn.vA, + dvmDetermineCat1Const((s4)decInsn.vB)); + break; + case OP_CONST_HIGH16: + /* could be boolean, int, float, or a null reference */ + setRegisterType(workRegs, decInsn.vA, + dvmDetermineCat1Const((s4) decInsn.vB << 16)); + break; + case OP_CONST_WIDE_16: + case OP_CONST_WIDE_32: + case OP_CONST_WIDE: + case OP_CONST_WIDE_HIGH16: + /* could be long or double; default to long and allow conversion */ + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_CONST_STRING: + case OP_CONST_STRING_JUMBO: + case OP_CONST_CLASS: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_MONITOR_ENTER: + case OP_MONITOR_EXIT: + break; + + case OP_CHECK_CAST: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + case OP_INSTANCE_OF: + /* result is boolean */ + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + + case OP_ARRAY_LENGTH: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + + case OP_NEW_INSTANCE: + case OP_NEW_ARRAY: + /* add the new uninitialized reference to the register ste */ + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + case OP_FILLED_NEW_ARRAY: + case OP_FILLED_NEW_ARRAY_RANGE: + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + kRegTypeReference); + justSetResult = true; + break; + + case OP_CMPL_FLOAT: + case OP_CMPG_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + case OP_CMPL_DOUBLE: + case OP_CMPG_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + case OP_CMP_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + + case OP_THROW: + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + case OP_PACKED_SWITCH: + case OP_SPARSE_SWITCH: + break; + + case OP_FILL_ARRAY_DATA: + break; + + case OP_IF_EQ: + case OP_IF_NE: + case OP_IF_LT: + case OP_IF_GE: + case OP_IF_GT: + case OP_IF_LE: + case OP_IF_EQZ: + case OP_IF_NEZ: + case OP_IF_LTZ: + case OP_IF_GEZ: + case OP_IF_GTZ: + case OP_IF_LEZ: + break; + + case OP_AGET: + tmpType = kRegTypeInteger; + goto aget_1nr_common; + case OP_AGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto aget_1nr_common; + case OP_AGET_BYTE: + tmpType = kRegTypeByte; + goto aget_1nr_common; + case OP_AGET_CHAR: + tmpType = kRegTypeChar; + goto aget_1nr_common; + case OP_AGET_SHORT: + tmpType = kRegTypeShort; + goto aget_1nr_common; +aget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_AGET_WIDE: + /* + * We know this is either long or double, and we don't really + * discriminate between those during verification, so we + * call it a long. + */ + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_AGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_APUT: + case OP_APUT_BOOLEAN: + case OP_APUT_BYTE: + case OP_APUT_CHAR: + case OP_APUT_SHORT: + case OP_APUT_WIDE: + case OP_APUT_OBJECT: + break; + + case OP_IGET: + tmpType = kRegTypeInteger; + goto iget_1nr_common; + case OP_IGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto iget_1nr_common; + case OP_IGET_BYTE: + tmpType = kRegTypeByte; + goto iget_1nr_common; + case OP_IGET_CHAR: + tmpType = kRegTypeChar; + goto iget_1nr_common; + case OP_IGET_SHORT: + tmpType = kRegTypeShort; + goto iget_1nr_common; +iget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_IGET_WIDE: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_IGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_IPUT: + case OP_IPUT_BOOLEAN: + case OP_IPUT_BYTE: + case OP_IPUT_CHAR: + case OP_IPUT_SHORT: + case OP_IPUT_WIDE: + case OP_IPUT_OBJECT: + break; + + case OP_SGET: + tmpType = kRegTypeInteger; + goto sget_1nr_common; + case OP_SGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto sget_1nr_common; + case OP_SGET_BYTE: + tmpType = kRegTypeByte; + goto sget_1nr_common; + case OP_SGET_CHAR: + tmpType = kRegTypeChar; + goto sget_1nr_common; + case OP_SGET_SHORT: + tmpType = kRegTypeShort; + goto sget_1nr_common; +sget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_SGET_WIDE: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_SGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_SPUT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_BYTE: + case OP_SPUT_CHAR: + case OP_SPUT_SHORT: + case OP_SPUT_WIDE: + case OP_SPUT_OBJECT: + break; + + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_VIRTUAL); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_DIRECT); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_STATIC); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + { + Method* absMethod; + + absMethod = getInvokedMethod(meth, &decInsn, METHOD_INTERFACE); + if (absMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(absMethod)); + justSetResult = true; + } + break; + + case OP_NEG_INT: + case OP_NOT_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_NEG_LONG: + case OP_NOT_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_NEG_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_NEG_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_INT_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_INT_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_INT_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_LONG_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_LONG_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_LONG_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_FLOAT_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_FLOAT_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_FLOAT_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_DOUBLE_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_DOUBLE_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_DOUBLE_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_INT_TO_BYTE: + setRegisterType(workRegs, decInsn.vA, kRegTypeByte); + break; + case OP_INT_TO_CHAR: + setRegisterType(workRegs, decInsn.vA, kRegTypeChar); + break; + case OP_INT_TO_SHORT: + setRegisterType(workRegs, decInsn.vA, kRegTypeShort); + break; + + case OP_ADD_INT: + case OP_SUB_INT: + case OP_MUL_INT: + case OP_REM_INT: + case OP_DIV_INT: + case OP_SHL_INT: + case OP_SHR_INT: + case OP_USHR_INT: + case OP_AND_INT: + case OP_OR_INT: + case OP_XOR_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_ADD_LONG: + case OP_SUB_LONG: + case OP_MUL_LONG: + case OP_DIV_LONG: + case OP_REM_LONG: + case OP_AND_LONG: + case OP_OR_LONG: + case OP_XOR_LONG: + case OP_SHL_LONG: + case OP_SHR_LONG: + case OP_USHR_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_ADD_FLOAT: + case OP_SUB_FLOAT: + case OP_MUL_FLOAT: + case OP_DIV_FLOAT: + case OP_REM_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_ADD_DOUBLE: + case OP_SUB_DOUBLE: + case OP_MUL_DOUBLE: + case OP_DIV_DOUBLE: + case OP_REM_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_ADD_INT_2ADDR: + case OP_SUB_INT_2ADDR: + case OP_MUL_INT_2ADDR: + case OP_REM_INT_2ADDR: + case OP_SHL_INT_2ADDR: + case OP_SHR_INT_2ADDR: + case OP_USHR_INT_2ADDR: + case OP_AND_INT_2ADDR: + case OP_OR_INT_2ADDR: + case OP_XOR_INT_2ADDR: + case OP_DIV_INT_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_ADD_LONG_2ADDR: + case OP_SUB_LONG_2ADDR: + case OP_MUL_LONG_2ADDR: + case OP_DIV_LONG_2ADDR: + case OP_REM_LONG_2ADDR: + case OP_AND_LONG_2ADDR: + case OP_OR_LONG_2ADDR: + case OP_XOR_LONG_2ADDR: + case OP_SHL_LONG_2ADDR: + case OP_SHR_LONG_2ADDR: + case OP_USHR_LONG_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_ADD_FLOAT_2ADDR: + case OP_SUB_FLOAT_2ADDR: + case OP_MUL_FLOAT_2ADDR: + case OP_DIV_FLOAT_2ADDR: + case OP_REM_FLOAT_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_ADD_DOUBLE_2ADDR: + case OP_SUB_DOUBLE_2ADDR: + case OP_MUL_DOUBLE_2ADDR: + case OP_DIV_DOUBLE_2ADDR: + case OP_REM_DOUBLE_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_ADD_INT_LIT16: + case OP_RSUB_INT: + case OP_MUL_INT_LIT16: + case OP_DIV_INT_LIT16: + case OP_REM_INT_LIT16: + case OP_AND_INT_LIT16: + case OP_OR_INT_LIT16: + case OP_XOR_INT_LIT16: + case OP_ADD_INT_LIT8: + case OP_RSUB_INT_LIT8: + case OP_MUL_INT_LIT8: + case OP_DIV_INT_LIT8: + case OP_REM_INT_LIT8: + case OP_SHL_INT_LIT8: + case OP_SHR_INT_LIT8: + case OP_USHR_INT_LIT8: + case OP_AND_INT_LIT8: + case OP_OR_INT_LIT8: + case OP_XOR_INT_LIT8: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + + + /* + * See comments in analysis/CodeVerify.c re: why some of these are + * annoying to deal with. In here, "annoying" turns into "impossible", + * since we make no effort to keep reference type info. + * + * Handling most of these would require retaining the field/method + * reference info that we discarded when the instructions were + * quickened. + */ + case OP_EXECUTE_INLINE: + case OP_INVOKE_DIRECT_EMPTY: + case OP_IGET_QUICK: + case OP_IGET_WIDE_QUICK: + case OP_IGET_OBJECT_QUICK: + case OP_IPUT_QUICK: + case OP_IPUT_WIDE_QUICK: + case OP_IPUT_OBJECT_QUICK: + case OP_INVOKE_VIRTUAL_QUICK: + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: + dvmAbort(); // can't work + break; + + + /* these should never appear */ + case OP_UNUSED_3E: + case OP_UNUSED_3F: + case OP_UNUSED_40: + case OP_UNUSED_41: + case OP_UNUSED_42: + case OP_UNUSED_43: + case OP_UNUSED_73: + case OP_UNUSED_79: + case OP_UNUSED_7A: + case OP_UNUSED_E3: + case OP_UNUSED_E4: + case OP_UNUSED_E5: + case OP_UNUSED_E6: + case OP_UNUSED_E7: + case OP_UNUSED_E8: + case OP_UNUSED_E9: + case OP_UNUSED_EA: + case OP_UNUSED_EB: + case OP_UNUSED_EC: + case OP_UNUSED_ED: + case OP_UNUSED_EF: + case OP_UNUSED_F1: + case OP_UNUSED_FC: + case OP_UNUSED_FD: + case OP_UNUSED_FE: + case OP_UNUSED_FF: + dvmAbort(); + break; /* * DO NOT add a "default" clause here. Without it the compiler will @@ -563,7 +1218,7 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, * the verifier. */ if (!justSetResult) { - int reg = RESULT_REGISTER(pState->insnRegCount); + int reg = RESULT_REGISTER(pState->insnRegCountPlus); workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown; } @@ -585,7 +1240,7 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, /* if not yet visited, or regs were updated, set "changed" */ if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) || compareRegisters(workRegs, entryRegs, - pState->insnRegCount) != 0) + pState->insnRegCountPlus) != 0) { dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true); } @@ -747,7 +1402,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, { const Method* meth = pState->method; InsnFlags* insnFlags = pState->insnFlags; - const int insnRegCount = pState->insnRegCount; + const int insnRegCountPlus = pState->insnRegCountPlus; SRegType* targetRegs = getRegisterLine(pState, nextInsn); if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) { @@ -759,7 +1414,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, * just an optimization.) */ LOGVV("COPY into 0x%04x\n", nextInsn); - copyRegisters(targetRegs, workRegs, insnRegCount); + copyRegisters(targetRegs, workRegs, insnRegCountPlus); dvmInsnSetChanged(insnFlags, nextInsn, true); } else { /* merge registers, set Changed only if different */ @@ -767,7 +1422,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, bool changed = false; int i; - for (i = 0; i < insnRegCount; i++) { + for (i = 0; i < insnRegCountPlus; i++) { targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed); } diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h index d431aa7ce..07071d2d4 100644 --- a/vm/analysis/RegisterMap.h +++ b/vm/analysis/RegisterMap.h @@ -15,17 +15,21 @@ */ /* - * Register map declaration. + * Declaration of register map data structure and related functions. */ #ifndef _DALVIK_REGISTERMAP #define _DALVIK_REGISTERMAP +/* + * This is a single variable-size structure. It may be allocated on the + * heap or mapped out of a DEX file. + */ typedef struct RegisterMap { /* header */ char addrWidth; /* bytes per address, 1 or 2 */ char regWidth; /* bytes per register line, 1+ */ - char pad0; - char pad1; + + /* char pad0, pad1; */ /* entries start here; 32-bit align guaranteed */ u4 entries[1]; diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c index 77134a733..8dcc6f8dc 100644 --- a/vm/analysis/VerifySubs.c +++ b/vm/analysis/VerifySubs.c @@ -195,6 +195,7 @@ bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags, const int insnCount = dvmGetMethodInsnsSize(meth); const u2* insns = meth->insns + curOffset; const u2* switchInsns; + u2 expectedSignature; int switchCount, tableSize; int offsetToSwitch, offsetToKeys, offsetToTargets, targ; int offset, absOffset; @@ -229,13 +230,22 @@ bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags, /* 0=sig, 1=count, 2/3=firstKey */ offsetToTargets = 4; offsetToKeys = -1; + expectedSignature = kPackedSwitchSignature; } else { /* 0=sig, 1=count, 2..count*2 = keys */ offsetToKeys = 2; offsetToTargets = 2 + 2*switchCount; + expectedSignature = kSparseSwitchSignature; } tableSize = offsetToTargets + switchCount*2; + if (switchInsns[0] != expectedSignature) { + LOG_VFY_METH(meth, + "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n", + switchInsns[0], expectedSignature); + return false; + } + /* make sure the end of the switch is in range */ if (curOffset + offsetToSwitch + tableSize > insnCount) { LOG_VFY_METH(meth, @@ -368,6 +378,9 @@ void dvmLogVerifyFailure(const Method* meth, const char* format, ...) /* * Show a relatively human-readable message describing the failure to * resolve a class. + * + * TODO: this is somewhat misleading when resolution fails because of + * illegal access rather than nonexistent class. */ void dvmLogUnableToResolveClass(const char* missingClassDescr, const Method* meth) @@ -434,4 +447,29 @@ bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags, return true; } +/* + * Given a 32-bit constant, return the most-restricted RegType enum entry + * that can hold the value. + */ +char dvmDetermineCat1Const(s4 value) +{ + if (value < -32768) + return kRegTypeInteger; + else if (value < -128) + return kRegTypeShort; + else if (value < 0) + return kRegTypeByte; + else if (value == 0) + return kRegTypeZero; + else if (value == 1) + return kRegTypeOne; + else if (value < 128) + return kRegTypePosByte; + else if (value < 32768) + return kRegTypePosShort; + else if (value < 65536) + return kRegTypeChar; + else + return kRegTypeInteger; +} diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h index 4a317d4ee..4d5b57ce8 100644 --- a/vm/analysis/VerifySubs.h +++ b/vm/analysis/VerifySubs.h @@ -67,4 +67,7 @@ void dvmLogUnableToResolveClass(const char* missingClassDescr, bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags, int curOffset, int* pOffset, bool* pConditional); +/* return a RegType enumeration value that "value" just fits into */ +char dvmDetermineCat1Const(s4 value); + #endif /*_DALVIK_VERIFYSUBS*/ |
