auto import from //branches/cupcake/...@127101
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 0b8a94d..2c5e749 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -327,32 +327,6 @@
}
/*
- * 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 @@
static RegType getMethodReturnType(const Method* meth)
{
RegType type;
- bool okay = true;
const char* descriptor = dexProtoGetReturnType(&meth->prototype);
switch (*descriptor) {
@@ -957,6 +930,7 @@
case 'L':
case '[':
{
+ bool okay = true;
ClassObject* clazz =
lookupClassByDescriptor(meth, descriptor, &okay);
assert(okay);
@@ -2455,7 +2429,7 @@
/* 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 @@
/*
+ * 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
case OP_INVOKE_INTERFACE:
case OP_INVOKE_INTERFACE_RANGE:
{
- RegType thisType, returnType;
+ RegType /*thisType,*/ returnType;
Method* absMethod;
bool isRange;
@@ -4666,6 +4716,7 @@
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 @@
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 @@
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 @@
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 90e4c6f..09199db 100644
--- a/vm/analysis/DexOptimize.c
+++ b/vm/analysis/DexOptimize.c
@@ -1657,7 +1657,7 @@
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 f78133b..5a3e8bd 100644
--- a/vm/analysis/DexVerify.c
+++ b/vm/analysis/DexVerify.c
@@ -331,13 +331,20 @@
*/
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 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 @@
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 8e0054e..eb243af 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 @@
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 @@
* 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 @@
* 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 @@
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 @@
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 @@
while (*ccp != 0) {
switch (*ccp) {
case 'L':
- case '[':
+ //case '[':
*pCurReg++ = kRegTypeReference;
break;
case 'Z':
@@ -386,7 +393,7 @@
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 @@
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 @@
*/
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 @@
}
/*
+ * 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 @@
* 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 @@
*/
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 @@
* 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 @@
/* 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 @@
{
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 @@
* 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 @@
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 d431aa7..07071d2 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 77134a7..8dcc6f8 100644
--- a/vm/analysis/VerifySubs.c
+++ b/vm/analysis/VerifySubs.c
@@ -195,6 +195,7 @@
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 @@
/* 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 @@
/*
* 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 @@
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 4a317d4..4d5b57c 100644
--- a/vm/analysis/VerifySubs.h
+++ b/vm/analysis/VerifySubs.h
@@ -67,4 +67,7 @@
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*/