Tons of fixes related to getting "verbose register info"/type analysis to work

git-svn-id: https://smali.googlecode.com/svn/trunk@621 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2010-02-11 00:58:02 +00:00
parent 7e24a9f010
commit c9be5e1303
11 changed files with 289 additions and 118 deletions

View File

@ -28,6 +28,7 @@
package org.jf.baksmali.Adaptors; package org.jf.baksmali.Adaptors;
import org.jf.dexlib.Code.Analysis.ValidationException;
import org.jf.dexlib.EncodedValue.EncodedValue; import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.*; import org.jf.dexlib.*;
import org.jf.dexlib.Code.Instruction; import org.jf.dexlib.Code.Instruction;
@ -50,6 +51,8 @@ public class ClassDefinition {
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor; private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
protected boolean validationErrors;
public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) { public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) {
this.stg = stg; this.stg = stg;
this.classDefItem = classDefItem; this.classDefItem = classDefItem;
@ -75,6 +78,10 @@ public class ClassDefinition {
return template; return template;
} }
public boolean hadValidationErrors() {
return validationErrors;
}
private void buildAnnotationMaps() { private void buildAnnotationMaps() {
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations(); AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
if (annotationDirectory == null) { if (annotationDirectory == null) {
@ -270,6 +277,13 @@ public class ClassDefinition {
MethodDefinition methodDefinition = new MethodDefinition(stg, method); MethodDefinition methodDefinition = new MethodDefinition(stg, method);
methodTemplates.add(methodDefinition.createTemplate(annotationSet, parameterAnnotationList)); methodTemplates.add(methodDefinition.createTemplate(annotationSet, parameterAnnotationList));
ValidationException validationException = methodDefinition.getValidationException();
if (validationException != null) {
//System.err.println(validationException.toString());
validationException.printStackTrace(System.err);
this.validationErrors = true;
}
} }
return methodTemplates; return methodTemplates;

View File

@ -34,6 +34,7 @@ import org.jf.dexlib.*;
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction; import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
import org.jf.dexlib.Code.Analysis.MethodAnalyzer; import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
import org.jf.dexlib.Code.Analysis.RegisterType; import org.jf.dexlib.Code.Analysis.RegisterType;
import org.jf.dexlib.Code.Analysis.ValidationException;
import org.jf.dexlib.Debug.DebugInstructionIterator; import org.jf.dexlib.Debug.DebugInstructionIterator;
import org.jf.dexlib.Code.Instruction; import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode; import org.jf.dexlib.Code.Opcode;
@ -75,11 +76,11 @@ public class MethodDefinition {
AnalyzedInstruction instruction = instructions[i]; AnalyzedInstruction instruction = instructions[i];
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) { if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
packedSwitchMap.append( packedSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(), currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
currentCodeAddress); currentCodeAddress);
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) { } else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
sparseSwitchMap.append( sparseSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(), currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
currentCodeAddress); currentCodeAddress);
} }
instructionMap.append(currentCodeAddress, i); instructionMap.append(currentCodeAddress, i);
@ -191,6 +192,14 @@ public class MethodDefinition {
return labelCache; return labelCache;
} }
public ValidationException getValidationException() {
if (methodAnalyzer == null) {
return null;
}
return methodAnalyzer.getValidationException();
}
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) { public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1); int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
@ -236,6 +245,12 @@ public class MethodDefinition {
AnalyzedInstruction[] instructions; AnalyzedInstruction[] instructions;
if (baksmali.verboseRegisterInfo) { if (baksmali.verboseRegisterInfo) {
instructions = methodAnalyzer.analyze(); instructions = methodAnalyzer.analyze();
ValidationException validationException = methodAnalyzer.getValidationException();
if (validationException != null) {
methodItems.add(new CommentMethodItem(stg, validationException.getMessage(),
validationException.getCodeAddress(), Integer.MIN_VALUE));
}
} else { } else {
instructions = methodAnalyzer.makeInstructionArray(); instructions = methodAnalyzer.makeInstructionArray();
} }

View File

@ -62,7 +62,7 @@ public class baksmali {
baksmali.bootClassPath = bootClassPath; baksmali.bootClassPath = bootClassPath;
if (verboseRegisterInfo) { if (verboseRegisterInfo) {
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile); ClassPath.InitializeClassPath(bootClassPath==null?null:bootClassPath.split(":"), dexFile);
} }
if (deodexerant != null) { if (deodexerant != null) {
@ -167,6 +167,11 @@ public class baksmali {
} }
} }
} }
//TODO: GROT
if (classDefinition.hadValidationErrors()) {
System.exit(1);
}
} }
} }
} }

View File

@ -325,7 +325,7 @@ public class main {
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath") Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
.withDescription("the bootclasspath jars to use, for analysis") .withDescription("the bootclasspath jars to use, for analysis")
.hasArg() .hasOptionalArg()
.withArgName("BOOTCLASSPATH") .withArgName("BOOTCLASSPATH")
.create("c"); .create("c");

View File

@ -147,12 +147,23 @@ public class AnalyzedInstruction {
//object register. If the uninitialized reference has been copied to other registers, they will be initialized //object register. If the uninitialized reference has been copied to other registers, they will be initialized
//as well, so we need to check for that too //as well, so we need to check for that too
if (isInvokeInit()) { if (isInvokeInit()) {
int destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD(); int destinationRegister;
if (instruction instanceof FiveRegisterInstruction) {
destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
} else {
assert instruction instanceof RegisterRangeInstruction;
RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
assert rangeInstruction.getRegCount() > 0;
destinationRegister = rangeInstruction.getStartRegister();
}
if (registerNumber == destinationRegister) { if (registerNumber == destinationRegister) {
return true; return true;
} }
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(destinationRegister); RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(registerNumber);
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef) { if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
return false; return false;
} }
//check if the uninit ref has been copied to another register //check if the uninit ref has been copied to another register

View File

@ -31,12 +31,10 @@ public class ClassPath {
} }
private void initClassPath(String[] bootClassPath, DexFile dexFile) { private void initClassPath(String[] bootClassPath, DexFile dexFile) {
if (bootClassPath == null || bootClassPath.length == 0) { if (bootClassPath != null) {
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given"); for (String bootClassPathEntry: bootClassPath) {
} loadBootClassPath(bootClassPathEntry);
}
for (String bootClassPathEntry: bootClassPath) {
loadBootClassPath(bootClassPathEntry);
} }
loadDexFile(dexFile); loadDexFile(dexFile);
@ -267,6 +265,46 @@ public class ClassPath {
public int getArrayDimensions() { public int getArrayDimensions() {
return arrayDimensions; return arrayDimensions;
} }
@Override
public boolean extendsClass(ClassDef superclassDef) {
if (!(superclassDef instanceof ArrayClassDef)) {
if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) {
return true;
} else if (superclassDef.isInterface) {
return this.implementsInterface(superclassDef);
}
return false;
}
ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef;
if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) {
ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
if (baseElementClass.isInterface) {
return true;
}
return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass());
} else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) {
ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
if (baseElementClass.isInterface) {
return true;
}
if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) {
return true;
}
return false;
}
return false;
}
@Override
public boolean implementsInterface(ClassDef interfaceDef) {
return interfaceDef.classType.equals("Ljava/lang/Cloneable;") ||
interfaceDef.classType.equals("Ljava/io/Serializable;");
}
} }
public static class PrimitiveClassDef extends ClassDef { public static class PrimitiveClassDef extends ClassDef {
@ -524,7 +562,7 @@ public class ClassPath {
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods(); EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
if (virtualMethods != null) { if (virtualMethods != null) {
for (EncodedMethod virtualMethod: virtualMethods) { for (EncodedMethod virtualMethod: virtualMethods) {
String methodString = virtualMethod.method.getMethodString(); String methodString = virtualMethod.method.getVirtualMethodString();
if (tempVirtualMethodLookup.get(methodString) == null) { if (tempVirtualMethodLookup.get(methodString) == null) {
virtualMethodList.add(methodString); virtualMethodList.add(methodString);
} }

View File

@ -15,7 +15,9 @@ public class MethodAnalyzer {
private boolean analyzed = false; private boolean analyzed = false;
private BitSet instructionsToVerify; private BitSet verifiedInstructions;
private ValidationException validationException = null;
//This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
//register types for this instruction to the parameter types, in order to have them propagate to all of its //register types for this instruction to the parameter types, in order to have them propagate to all of its
@ -58,7 +60,7 @@ public class MethodAnalyzer {
buildInstructionList(); buildInstructionList();
instructionsToVerify = new BitSet(instructions.size()); verifiedInstructions = new BitSet(instructions.size());
} }
public AnalyzedInstruction[] analyze() { public AnalyzedInstruction[] analyze() {
@ -88,7 +90,7 @@ public class MethodAnalyzer {
} }
setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
RegisterType.getRegisterType(RegisterType.Category.UninitRef, RegisterType.getRegisterType(RegisterType.Category.UninitThis,
ClassPath.getClassDef(methodIdItem.getContainingClass()))); ClassPath.getClassDef(methodIdItem.getContainingClass())));
} else { } else {
if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) { if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
@ -111,17 +113,40 @@ public class MethodAnalyzer {
} }
} }
BitSet analyzedInstructions = new BitSet(instructionsToVerify.size()); BitSet instructionsToAnalyze = new BitSet(verifiedInstructions.size());
//make sure all of the "first instructions" are marked for processing //make sure all of the "first instructions" are marked for processing
for (AnalyzedInstruction successor: startOfMethod.successors) { for (AnalyzedInstruction successor: startOfMethod.successors) {
instructionsToVerify.set(successor.instructionIndex); instructionsToAnalyze.set(successor.instructionIndex);
} }
while (!instructionsToVerify.isEmpty()) { while (!instructionsToAnalyze.isEmpty()) {
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) { for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
instructionsToVerify.clear(i); instructionsToAnalyze.clear(i);
analyzeInstruction(instructions.valueAt(i)); if (verifiedInstructions.get(i)) {
continue;
}
AnalyzedInstruction instructionToVerify = instructions.valueAt(i);
try {
analyzeInstruction(instructionToVerify);
} catch (ValidationException ex) {
this.validationException = ex;
int codeAddress = getInstructionAddress(instructionToVerify);
ex.setCodeAddress(codeAddress);
ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name));
ex.addContext(String.format("CodeAddress: %d", codeAddress));
ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
break;
}
verifiedInstructions.set(instructionToVerify.getInstructionIndex());
for (AnalyzedInstruction successor: instructionToVerify.successors) {
instructionsToAnalyze.set(successor.getInstructionIndex());
}
}
if (validationException != null) {
break;
} }
} }
@ -166,6 +191,10 @@ public class MethodAnalyzer {
return instructionArray; return instructionArray;
} }
public ValidationException getValidationException() {
return validationException;
}
private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
assert typeListItem != null; assert typeListItem != null;
assert parameterRegisterCount == typeListItem.getRegisterCount(); assert parameterRegisterCount == typeListItem.getRegisterCount();
@ -178,7 +207,7 @@ public class MethodAnalyzer {
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
} else { } else {
registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type); registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
} }
} }
@ -250,7 +279,7 @@ public class MethodAnalyzer {
if (successor.setPostRegisterType(registerNumber, registerType)) { if (successor.setPostRegisterType(registerNumber, registerType)) {
changedInstructions.set(successor.instructionIndex); changedInstructions.set(successor.instructionIndex);
instructionsToVerify.set(successor.instructionIndex); verifiedInstructions.clear(successor.instructionIndex);
} }
} }
} }
@ -288,6 +317,7 @@ public class MethodAnalyzer {
for (int i=0; i<instructions.size(); i++) { for (int i=0; i<instructions.size(); i++) {
AnalyzedInstruction instruction = instructions.valueAt(i); AnalyzedInstruction instruction = instructions.valueAt(i);
Opcode instructionOpcode = instruction.instruction.opcode; Opcode instructionOpcode = instruction.instruction.opcode;
currentCodeAddress = getInstructionAddress(instruction);
//check if we have gone past the end of the current try //check if we have gone past the end of the current try
if (currentTry != null) { if (currentTry != null) {
@ -328,11 +358,16 @@ public class MethodAnalyzer {
int instructionCodeAddress = getInstructionAddress(instruction); int instructionCodeAddress = getInstructionAddress(instruction);
if (instruction.instruction.opcode.canContinue()) { if (instruction.instruction.opcode.canContinue()) {
if (i == instructions.size() - 1) { if (instruction.instruction.opcode != Opcode.NOP ||
throw new ValidationException("Execution can continue past the last instruction"); !instruction.instruction.getFormat().variableSizeFormat) {
if (i == instructions.size() - 1) {
throw new ValidationException("Execution can continue past the last instruction");
}
AnalyzedInstruction nextInstruction = instructions.valueAt(i+1);
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
} }
AnalyzedInstruction nextInstruction = instructions.valueAt(i+1);
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
} }
if (instruction.instruction instanceof OffsetInstruction) { if (instruction.instruction instanceof OffsetInstruction) {
@ -387,7 +422,7 @@ public class MethodAnalyzer {
if (exceptionHandlersForSuccessor != null) { if (exceptionHandlersForSuccessor != null) {
//the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
//can throw an exception //can throw an exception
assert predecessor.instruction.opcode.canThrow(); assert successor.instruction.opcode.canThrow();
for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true); addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true);
@ -778,7 +813,7 @@ public class MethodAnalyzer {
case XOR_INT: case XOR_INT:
handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories, handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
RegisterType.Category.Integer, true); RegisterType.Category.Integer, true);
return;
case ADD_LONG: case ADD_LONG:
case SUB_LONG: case SUB_LONG:
case MUL_LONG: case MUL_LONG:
@ -928,9 +963,15 @@ public class MethodAnalyzer {
RegisterType.Category.Null, RegisterType.Category.Null,
RegisterType.Category.Reference); RegisterType.Category.Reference);
private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of(
RegisterType.Category.Null,
RegisterType.Category.UninitThis,
RegisterType.Category.Reference);
private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of( private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
RegisterType.Category.Null, RegisterType.Category.Null,
RegisterType.Category.UninitRef, RegisterType.Category.UninitRef,
RegisterType.Category.UninitThis,
RegisterType.Category.Reference); RegisterType.Category.Reference);
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of( private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
@ -1036,7 +1077,8 @@ public class MethodAnalyzer {
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
} }
private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) { //TODO: GROT
/*private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
assert this.isInstanceConstructor(); assert this.isInstanceConstructor();
//if we're in an instance constructor (an <init> method), then the superclass <init> must have been called. //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called.
@ -1053,12 +1095,12 @@ public class MethodAnalyzer {
//TODO: GROT //TODO: GROT
//assert thisRegisterType.category == RegisterType.Category.Reference; //assert thisRegisterType.category == RegisterType.Category.Reference;
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass()); //assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
} }*/
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) { private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
if (this.isInstanceConstructor()) { /*if (this.isInstanceConstructor()) {
checkConstructorReturn(analyzedInstruction); checkConstructorReturn(analyzedInstruction);
} }*/
TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
if (returnType.getTypeDescriptor().charAt(0) != 'V') { if (returnType.getTypeDescriptor().charAt(0) != 'V') {
@ -1069,9 +1111,9 @@ public class MethodAnalyzer {
} }
private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
if (this.isInstanceConstructor()) { /*if (this.isInstanceConstructor()) {
checkConstructorReturn(analyzedInstruction); checkConstructorReturn(analyzedInstruction);
} }*/
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
int returnRegister = instruction.getRegisterA(); int returnRegister = instruction.getRegisterA();
@ -1093,11 +1135,14 @@ public class MethodAnalyzer {
if (validCategories == ReferenceCategories) { if (validCategories == ReferenceCategories) {
if (methodReturnRegisterType.type.isInterface()) { if (methodReturnRegisterType.type.isInterface()) {
if (!returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) { if (returnRegisterType.category != RegisterType.Category.Null &&
!returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
//TODO: how to handle warnings? //TODO: how to handle warnings?
} }
} else { } else {
if (!returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) { if (returnRegisterType.category == RegisterType.Category.Reference &&
!returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
throw new ValidationException(String.format("The return value in register v%d (%s) is not " + throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
"compatible with the method's return type %s", returnRegister, "compatible with the method's return type %s", returnRegister,
returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType())); returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
@ -1147,9 +1192,11 @@ public class MethodAnalyzer {
Item item = instruction.getReferencedItem(); Item item = instruction.getReferencedItem();
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
//make sure the referenced class is resolvable
//TODO: need to check class access //TODO: need to check class access
ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); //make sure the referenced class is resolvable
ClassPath.getClassDef((TypeIdItem)item);
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
} }
private void handleMonitor(AnalyzedInstruction analyzedInstruction) { private void handleMonitor(AnalyzedInstruction analyzedInstruction) {
@ -1224,10 +1271,9 @@ public class MethodAnalyzer {
throw new ValidationException(String.format("Cannot use array-length with non-array type %s", throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
arrayRegisterType.type.getClassType())); arrayRegisterType.type.getClassType()));
} }
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
} }
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
RegisterType.getRegisterType(RegisterType.Category.Integer, null)); RegisterType.getRegisterType(RegisterType.Category.Integer, null));
} }
@ -1255,6 +1301,8 @@ public class MethodAnalyzer {
"that was created by this new-instance instruction.", i)); "that was created by this new-instance instruction.", i));
} }
} }
return;
} }
Item item = instruction.getReferencedItem(); Item item = instruction.getReferencedItem();
@ -1327,7 +1375,7 @@ public class MethodAnalyzer {
public boolean moveNext() { public boolean moveNext() {
currentRegister++; currentRegister++;
return pastEnd(); return !pastEnd();
} }
public int getCount() { public int getCount() {
@ -1355,7 +1403,7 @@ public class MethodAnalyzer {
public boolean moveNext() { public boolean moveNext() {
currentRegister++; currentRegister++;
return pastEnd(); return !pastEnd();
} }
public int getCount() { public int getCount() {
@ -1405,8 +1453,6 @@ public class MethodAnalyzer {
arrayType.type.getClassType()); arrayType.type.getClassType());
} }
} while (registerIterator.moveNext()); } while (registerIterator.moveNext());
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
} }
private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) { private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
@ -1619,7 +1665,7 @@ public class MethodAnalyzer {
RegisterType arrayBaseType = RegisterType arrayBaseType =
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
"for the instruction.", analyzedInstruction.instruction.opcode.name, "for the instruction.", analyzedInstruction.instruction.opcode.name,
arrayRegisterType.type.getClassType())); arrayRegisterType.type.getClassType()));
@ -1753,7 +1799,7 @@ public class MethodAnalyzer {
RegisterType arrayBaseType = RegisterType arrayBaseType =
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
"for the instruction.", analyzedInstruction.instruction.opcode.name, "for the instruction.", analyzedInstruction.instruction.opcode.name,
arrayRegisterType.type.getClassType())); arrayRegisterType.type.getClassType()));
@ -1842,10 +1888,9 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -1872,10 +1917,9 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -1901,10 +1945,9 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -1931,7 +1974,7 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
assert sourceRegisterType != null; assert sourceRegisterType != null;
@ -1952,7 +1995,6 @@ public class MethodAnalyzer {
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -1976,12 +2018,11 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -2005,13 +2046,12 @@ public class MethodAnalyzer {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
ReferenceCategories); ReferenceOrUninitThisCategories);
RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
ReferenceCategories); ReferenceCategories);
//TODO: check access //TODO: check access
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
assert referencedItem instanceof FieldIdItem; assert referencedItem instanceof FieldIdItem;
FieldIdItem field = (FieldIdItem)referencedItem; FieldIdItem field = (FieldIdItem)referencedItem;
@ -2148,8 +2188,6 @@ public class MethodAnalyzer {
"for the instruction.", analyzedInstruction.instruction.opcode.name, "for the instruction.", analyzedInstruction.instruction.opcode.name,
field.getFieldString())); field.getFieldString()));
} }
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
} }
private void handleSputObject(AnalyzedInstruction analyzedInstruction) { private void handleSputObject(AnalyzedInstruction analyzedInstruction) {
@ -2235,13 +2273,21 @@ public class MethodAnalyzer {
} }
if ((invokeType & INVOKE_SUPER) != 0) { if ((invokeType & INVOKE_SUPER) != 0) {
if (methodClassDef.getSuperclass() == null) { ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass());
if (currentMethodClassDef.getSuperclass() == null) {
throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass", throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name, methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
methodClassDef.getSuperclass().getClassType())); methodClassDef.getSuperclass().getClassType()));
} }
if (!methodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getMethodString())) { if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) {
throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " +
"of the current class %s", methodIdItem.getMethodString(),
analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(),
encodedMethod.method.getContainingClass().getTypeDescriptor()));
}
if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getVirtualMethodString())) {
throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" + throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
"no such method", methodIdItem.getMethodString(), "no such method", methodIdItem.getMethodString(),
analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType())); analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
@ -2276,7 +2322,9 @@ public class MethodAnalyzer {
objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
assert objectRegisterType != null; assert objectRegisterType != null;
if (objectRegisterType.category == RegisterType.Category.UninitRef) { if (objectRegisterType.category == RegisterType.Category.UninitRef ||
objectRegisterType.category == RegisterType.Category.UninitThis) {
if (!isInit) { if (!isInit) {
throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " + throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
"reference type %s", methodIdItem.getMethodString(), "reference type %s", methodIdItem.getMethodString(),
@ -2308,8 +2356,9 @@ public class MethodAnalyzer {
} }
} }
if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null &&
!objectRegisterType.type.extendsClass(methodClassDef)) {
if ((invokeType & INVOKE_INTERFACE) == 0 && !objectRegisterType.type.extendsClass(methodClassDef)) {
throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " + throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
"does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(), "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
methodClassDef.getClassType())); methodClassDef.getClassType()));
@ -2370,7 +2419,9 @@ public class MethodAnalyzer {
RegisterType preInstructionRegisterType = RegisterType preInstructionRegisterType =
analyzedInstruction.getMergedRegisterTypeFromPredecessors(i); analyzedInstruction.getMergedRegisterTypeFromPredecessors(i);
if (preInstructionRegisterType.category == RegisterType.Category.UninitRef) { if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
RegisterType registerType = null; RegisterType registerType = null;
if (preInstructionRegisterType == objectRegisterType) { if (preInstructionRegisterType == objectRegisterType) {
registerType = analyzedInstruction.postRegisterMap[objectRegister]; registerType = analyzedInstruction.postRegisterMap[objectRegister];

View File

@ -15,8 +15,10 @@ public class RegisterType {
public final ClassDef type; public final ClassDef type;
private RegisterType(Category category, ClassDef type) { private RegisterType(Category category, ClassDef type) {
assert ((category == Category.Reference || category == Category.UninitRef) && type != null) || assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
((category != Category.Reference && category != Category.UninitRef) && type == null); type != null) ||
((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
type == null);
this.category = category; this.category = category;
this.type = type; this.type = type;
@ -65,9 +67,10 @@ public class RegisterType {
DoubleLo, DoubleLo,
DoubleHi, DoubleHi,
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called //the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
//it is also used for the "this" register inside an <init> method, before the superclass' <init> method is
//called
UninitRef, UninitRef,
//the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
//method is called
UninitThis,
Reference, Reference,
//This is used when there are multiple incoming execution paths that have incompatible register types. For //This is used when there are multiple incoming execution paths that have incompatible register types. For
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another //example if the register's type is an Integer on one incomming code path, but is a Reference type on another
@ -79,25 +82,26 @@ public class RegisterType {
//could hold either type of value. //could hold either type of value.
protected static Category[][] mergeTable = protected static Category[][] mergeTable =
{ {
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted*/ /* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/
/*Unknown*/ {Unknown, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, Reference, Conflicted}, /*Unknown*/ {Unknown, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, Reference, Conflicted},
/*Null*/ {Null, Null, Conflicted, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted}, /*Null*/ {Null, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*One*/ {One, Conflicted, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*One*/ {One, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Boolean*/ {Boolean, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Boolean*/ {Boolean, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Byte*/ {Byte, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Byte*/ {Byte, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*PosByte*/ {PosByte, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*PosByte*/ {PosByte, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Short*/ {Short, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Short*/ {Short, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*PosShort*/ {PosShort, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*PosShort*/ {PosShort, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Char*/ {Char, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Char*/ {Char, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Integer*/ {Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Integer*/ {Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Float*/ {Float, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*Float*/ {Float, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted}, /*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted}, /*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted}, /*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted}, /*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, /*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Reference*/ {Reference, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted}, /*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted},
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted} /*Reference*/ {Reference, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
}; };
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For //this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
@ -107,25 +111,26 @@ public class RegisterType {
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing. //check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
protected static boolean[][] assigmentTable = protected static boolean[][] assigmentTable =
{ {
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted |slot type*/ /* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, /*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Null*/ {false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, true, false}, /*Null*/ {false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false},
/*One*/ {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false}, /*One*/ {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Boolean*/ {false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false}, /*Boolean*/ {false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Byte*/ {false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false}, /*Byte*/ {false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false},
/*PosByte*/ {false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false}, /*PosByte*/ {false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Short*/ {false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false}, /*Short*/ {false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false},
/*PosShort*/ {false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false}, /*PosShort*/ {false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Char*/ {false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false}, /*Char*/ {false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false},
/*Integer*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false}, /*Integer*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*Float*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false}, /*Float*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false}, /*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false}, /*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false}, /*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false}, /*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, /*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, /*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false} /*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
/*----------*/ /*----------*/
/*value type*/ /*value type*/
}; };
@ -227,7 +232,7 @@ public class RegisterType {
if (mergedCategory == Category.Reference) { if (mergedCategory == Category.Reference) {
mergedType = ClassPath.getCommonSuperclass(this.type, type.type); mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
} }
if (mergedCategory == Category.UninitRef) { if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
if (this.category == Category.Unknown) { if (this.category == Category.Unknown) {
return type; return type;
} }

View File

@ -3,7 +3,22 @@ package org.jf.dexlib.Code.Analysis;
import org.jf.dexlib.Util.ExceptionWithContext; import org.jf.dexlib.Util.ExceptionWithContext;
public class ValidationException extends ExceptionWithContext { public class ValidationException extends ExceptionWithContext {
private int codeAddress;
public ValidationException(int codeAddress, String errorMessage) {
super(errorMessage);
this.codeAddress = codeAddress;
}
public ValidationException(String errorMessage) { public ValidationException(String errorMessage) {
super(errorMessage); super(errorMessage);
} }
public void setCodeAddress(int codeAddress) {
this.codeAddress = codeAddress;
}
public int getCodeAddress() {
return codeAddress;
}
} }

View File

@ -73,7 +73,7 @@ public enum Opcode
FILLED_NEW_ARRAY((byte)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), FILLED_NEW_ARRAY((byte)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
FILLED_NEW_ARRAY_RANGE((byte)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), FILLED_NEW_ARRAY_RANGE((byte)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
FILL_ARRAY_DATA((byte)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE), FILL_ARRAY_DATA((byte)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
THROW((byte)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), THROW((byte)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW),
GOTO((byte)0x28, "goto", ReferenceType.none, Format.Format10t), GOTO((byte)0x28, "goto", ReferenceType.none, Format.Format10t),
GOTO_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t), GOTO_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t),
GOTO_32((byte)0x2a, "goto/32", ReferenceType.none, Format.Format30t), GOTO_32((byte)0x2a, "goto/32", ReferenceType.none, Format.Format30t),
@ -223,11 +223,11 @@ public enum Opcode
SHL_LONG_2ADDR((byte)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), SHL_LONG_2ADDR((byte)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
SHR_LONG_2ADDR((byte)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), SHR_LONG_2ADDR((byte)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
USHR_LONG_2ADDR((byte)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), USHR_LONG_2ADDR((byte)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
ADD_FLOAT_2ADDR((byte)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), ADD_FLOAT_2ADDR((byte)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
SUB_FLOAT_2ADDR((byte)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), SUB_FLOAT_2ADDR((byte)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
MUL_FLOAT_2ADDR((byte)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), MUL_FLOAT_2ADDR((byte)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
DIV_FLOAT_2ADDR((byte)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), DIV_FLOAT_2ADDR((byte)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
REM_FLOAT_2ADDR((byte)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), REM_FLOAT_2ADDR((byte)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
ADD_DOUBLE_2ADDR((byte)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), ADD_DOUBLE_2ADDR((byte)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
SUB_DOUBLE_2ADDR((byte)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), SUB_DOUBLE_2ADDR((byte)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
MUL_DOUBLE_2ADDR((byte)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), MUL_DOUBLE_2ADDR((byte)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),

View File

@ -147,6 +147,23 @@ public class MethodIdItem extends Item<MethodIdItem> {
return cachedMethodString; return cachedMethodString;
} }
private String cachedVirtualMethodString = null;
/**
* @return a string formatted like methodName(TTTT..)R
*/
public String getVirtualMethodString() {
if (cachedVirtualMethodString == null) {
String methodName = this.methodName.getStringValue();
String prototypeString = methodPrototype.getPrototypeString();
StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length());
sb.append(methodName);
sb.append(prototypeString);
cachedVirtualMethodString = sb.toString();
}
return cachedVirtualMethodString;
}
/** /**
* @return the method prototype * @return the method prototype
*/ */