mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 12:20:11 +02:00
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:
parent
7e24a9f010
commit
c9be5e1303
@ -28,6 +28,7 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||
import org.jf.dexlib.EncodedValue.EncodedValue;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
@ -50,6 +51,8 @@ public class ClassDefinition {
|
||||
|
||||
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
|
||||
|
||||
protected boolean validationErrors;
|
||||
|
||||
public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) {
|
||||
this.stg = stg;
|
||||
this.classDefItem = classDefItem;
|
||||
@ -75,6 +78,10 @@ public class ClassDefinition {
|
||||
return template;
|
||||
}
|
||||
|
||||
public boolean hadValidationErrors() {
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
private void buildAnnotationMaps() {
|
||||
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
|
||||
if (annotationDirectory == null) {
|
||||
@ -270,6 +277,13 @@ public class ClassDefinition {
|
||||
MethodDefinition methodDefinition = new MethodDefinition(stg, method);
|
||||
|
||||
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;
|
||||
|
@ -34,6 +34,7 @@ import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib.Code.Analysis.RegisterType;
|
||||
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
@ -75,11 +76,11 @@ public class MethodDefinition {
|
||||
AnalyzedInstruction instruction = instructions[i];
|
||||
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||
packedSwitchMap.append(
|
||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||
sparseSwitchMap.append(
|
||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
}
|
||||
instructionMap.append(currentCodeAddress, i);
|
||||
@ -191,6 +192,14 @@ public class MethodDefinition {
|
||||
return labelCache;
|
||||
}
|
||||
|
||||
public ValidationException getValidationException() {
|
||||
if (methodAnalyzer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return methodAnalyzer.getValidationException();
|
||||
}
|
||||
|
||||
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
|
||||
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
|
||||
|
||||
@ -236,6 +245,12 @@ public class MethodDefinition {
|
||||
AnalyzedInstruction[] instructions;
|
||||
if (baksmali.verboseRegisterInfo) {
|
||||
instructions = methodAnalyzer.analyze();
|
||||
|
||||
ValidationException validationException = methodAnalyzer.getValidationException();
|
||||
if (validationException != null) {
|
||||
methodItems.add(new CommentMethodItem(stg, validationException.getMessage(),
|
||||
validationException.getCodeAddress(), Integer.MIN_VALUE));
|
||||
}
|
||||
} else {
|
||||
instructions = methodAnalyzer.makeInstructionArray();
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class baksmali {
|
||||
baksmali.bootClassPath = bootClassPath;
|
||||
|
||||
if (verboseRegisterInfo) {
|
||||
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
|
||||
ClassPath.InitializeClassPath(bootClassPath==null?null:bootClassPath.split(":"), dexFile);
|
||||
}
|
||||
|
||||
if (deodexerant != null) {
|
||||
@ -167,6 +167,11 @@ public class baksmali {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: GROT
|
||||
if (classDefinition.hadValidationErrors()) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ public class main {
|
||||
|
||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
||||
.withDescription("the bootclasspath jars to use, for analysis")
|
||||
.hasArg()
|
||||
.hasOptionalArg()
|
||||
.withArgName("BOOTCLASSPATH")
|
||||
.create("c");
|
||||
|
||||
|
@ -147,12 +147,23 @@ public class AnalyzedInstruction {
|
||||
//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
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(destinationRegister);
|
||||
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef) {
|
||||
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(registerNumber);
|
||||
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
|
||||
preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
|
||||
|
||||
return false;
|
||||
}
|
||||
//check if the uninit ref has been copied to another register
|
||||
|
@ -31,12 +31,10 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
private void initClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||
if (bootClassPath == null || bootClassPath.length == 0) {
|
||||
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
|
||||
}
|
||||
|
||||
for (String bootClassPathEntry: bootClassPath) {
|
||||
loadBootClassPath(bootClassPathEntry);
|
||||
if (bootClassPath != null) {
|
||||
for (String bootClassPathEntry: bootClassPath) {
|
||||
loadBootClassPath(bootClassPathEntry);
|
||||
}
|
||||
}
|
||||
|
||||
loadDexFile(dexFile);
|
||||
@ -267,6 +265,46 @@ public class ClassPath {
|
||||
public int getArrayDimensions() {
|
||||
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 {
|
||||
@ -524,7 +562,7 @@ public class ClassPath {
|
||||
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
|
||||
if (virtualMethods != null) {
|
||||
for (EncodedMethod virtualMethod: virtualMethods) {
|
||||
String methodString = virtualMethod.method.getMethodString();
|
||||
String methodString = virtualMethod.method.getVirtualMethodString();
|
||||
if (tempVirtualMethodLookup.get(methodString) == null) {
|
||||
virtualMethodList.add(methodString);
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ public class MethodAnalyzer {
|
||||
|
||||
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
|
||||
//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();
|
||||
|
||||
instructionsToVerify = new BitSet(instructions.size());
|
||||
verifiedInstructions = new BitSet(instructions.size());
|
||||
}
|
||||
|
||||
public AnalyzedInstruction[] analyze() {
|
||||
@ -88,7 +90,7 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
|
||||
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
|
||||
RegisterType.getRegisterType(RegisterType.Category.UninitThis,
|
||||
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
||||
} else {
|
||||
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
|
||||
for (AnalyzedInstruction successor: startOfMethod.successors) {
|
||||
instructionsToVerify.set(successor.instructionIndex);
|
||||
instructionsToAnalyze.set(successor.instructionIndex);
|
||||
}
|
||||
|
||||
while (!instructionsToVerify.isEmpty()) {
|
||||
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
|
||||
instructionsToVerify.clear(i);
|
||||
analyzeInstruction(instructions.valueAt(i));
|
||||
while (!instructionsToAnalyze.isEmpty()) {
|
||||
for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
|
||||
instructionsToAnalyze.clear(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;
|
||||
}
|
||||
|
||||
public ValidationException getValidationException() {
|
||||
return validationException;
|
||||
}
|
||||
|
||||
private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
|
||||
assert typeListItem != null;
|
||||
assert parameterRegisterCount == typeListItem.getRegisterCount();
|
||||
@ -178,7 +207,7 @@ public class MethodAnalyzer {
|
||||
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
|
||||
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
|
||||
} else {
|
||||
registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type);
|
||||
registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,7 +279,7 @@ public class MethodAnalyzer {
|
||||
|
||||
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
||||
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++) {
|
||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||
currentCodeAddress = getInstructionAddress(instruction);
|
||||
|
||||
//check if we have gone past the end of the current try
|
||||
if (currentTry != null) {
|
||||
@ -328,11 +358,16 @@ public class MethodAnalyzer {
|
||||
int instructionCodeAddress = getInstructionAddress(instruction);
|
||||
|
||||
if (instruction.instruction.opcode.canContinue()) {
|
||||
if (i == instructions.size() - 1) {
|
||||
throw new ValidationException("Execution can continue past the last instruction");
|
||||
if (instruction.instruction.opcode != Opcode.NOP ||
|
||||
!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) {
|
||||
@ -387,7 +422,7 @@ public class MethodAnalyzer {
|
||||
if (exceptionHandlersForSuccessor != null) {
|
||||
//the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
|
||||
//can throw an exception
|
||||
assert predecessor.instruction.opcode.canThrow();
|
||||
assert successor.instruction.opcode.canThrow();
|
||||
|
||||
for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
|
||||
addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true);
|
||||
@ -778,7 +813,7 @@ public class MethodAnalyzer {
|
||||
case XOR_INT:
|
||||
handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
|
||||
RegisterType.Category.Integer, true);
|
||||
|
||||
return;
|
||||
case ADD_LONG:
|
||||
case SUB_LONG:
|
||||
case MUL_LONG:
|
||||
@ -928,9 +963,15 @@ public class MethodAnalyzer {
|
||||
RegisterType.Category.Null,
|
||||
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(
|
||||
RegisterType.Category.Null,
|
||||
RegisterType.Category.UninitRef,
|
||||
RegisterType.Category.UninitThis,
|
||||
RegisterType.Category.Reference);
|
||||
|
||||
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
||||
@ -1036,7 +1077,8 @@ public class MethodAnalyzer {
|
||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
|
||||
}
|
||||
|
||||
private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
|
||||
//TODO: GROT
|
||||
/*private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
|
||||
assert this.isInstanceConstructor();
|
||||
|
||||
//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
|
||||
//assert thisRegisterType.category == RegisterType.Category.Reference;
|
||||
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||
}
|
||||
}*/
|
||||
|
||||
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
||||
if (this.isInstanceConstructor()) {
|
||||
/*if (this.isInstanceConstructor()) {
|
||||
checkConstructorReturn(analyzedInstruction);
|
||||
}
|
||||
}*/
|
||||
|
||||
TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
|
||||
if (returnType.getTypeDescriptor().charAt(0) != 'V') {
|
||||
@ -1069,9 +1111,9 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
|
||||
if (this.isInstanceConstructor()) {
|
||||
/*if (this.isInstanceConstructor()) {
|
||||
checkConstructorReturn(analyzedInstruction);
|
||||
}
|
||||
}*/
|
||||
|
||||
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
|
||||
int returnRegister = instruction.getRegisterA();
|
||||
@ -1093,11 +1135,14 @@ public class MethodAnalyzer {
|
||||
|
||||
if (validCategories == ReferenceCategories) {
|
||||
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?
|
||||
}
|
||||
} 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 " +
|
||||
"compatible with the method's return type %s", returnRegister,
|
||||
returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
|
||||
@ -1147,9 +1192,11 @@ public class MethodAnalyzer {
|
||||
Item item = instruction.getReferencedItem();
|
||||
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
|
||||
|
||||
//make sure the referenced class is resolvable
|
||||
//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) {
|
||||
@ -1224,10 +1271,9 @@ public class MethodAnalyzer {
|
||||
throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
|
||||
arrayRegisterType.type.getClassType()));
|
||||
}
|
||||
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
|
||||
}
|
||||
|
||||
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
|
||||
|
||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
|
||||
RegisterType.getRegisterType(RegisterType.Category.Integer, null));
|
||||
}
|
||||
@ -1255,6 +1301,8 @@ public class MethodAnalyzer {
|
||||
"that was created by this new-instance instruction.", i));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = instruction.getReferencedItem();
|
||||
@ -1327,7 +1375,7 @@ public class MethodAnalyzer {
|
||||
|
||||
public boolean moveNext() {
|
||||
currentRegister++;
|
||||
return pastEnd();
|
||||
return !pastEnd();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
@ -1355,7 +1403,7 @@ public class MethodAnalyzer {
|
||||
|
||||
public boolean moveNext() {
|
||||
currentRegister++;
|
||||
return pastEnd();
|
||||
return !pastEnd();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
@ -1405,8 +1453,6 @@ public class MethodAnalyzer {
|
||||
arrayType.type.getClassType());
|
||||
}
|
||||
} while (registerIterator.moveNext());
|
||||
|
||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
|
||||
}
|
||||
|
||||
private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
|
||||
@ -1619,7 +1665,7 @@ public class MethodAnalyzer {
|
||||
|
||||
RegisterType arrayBaseType =
|
||||
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 " +
|
||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||
arrayRegisterType.type.getClassType()));
|
||||
@ -1753,7 +1799,7 @@ public class MethodAnalyzer {
|
||||
|
||||
RegisterType arrayBaseType =
|
||||
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 " +
|
||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||
arrayRegisterType.type.getClassType()));
|
||||
@ -1842,10 +1888,9 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -1872,10 +1917,9 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -1901,10 +1945,9 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -1931,7 +1974,7 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
|
||||
assert sourceRegisterType != null;
|
||||
@ -1952,7 +1995,6 @@ public class MethodAnalyzer {
|
||||
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -1976,12 +2018,11 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -2005,13 +2046,12 @@ public class MethodAnalyzer {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
ReferenceCategories);
|
||||
ReferenceOrUninitThisCategories);
|
||||
|
||||
RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||
RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
|
||||
ReferenceCategories);
|
||||
|
||||
//TODO: check access
|
||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||
assert referencedItem instanceof FieldIdItem;
|
||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||
@ -2148,8 +2188,6 @@ public class MethodAnalyzer {
|
||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||
field.getFieldString()));
|
||||
}
|
||||
|
||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
|
||||
}
|
||||
|
||||
private void handleSputObject(AnalyzedInstruction analyzedInstruction) {
|
||||
@ -2235,13 +2273,21 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
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",
|
||||
methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
|
||||
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" +
|
||||
"no such method", methodIdItem.getMethodString(),
|
||||
analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
|
||||
@ -2276,7 +2322,9 @@ public class MethodAnalyzer {
|
||||
|
||||
objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
|
||||
assert objectRegisterType != null;
|
||||
if (objectRegisterType.category == RegisterType.Category.UninitRef) {
|
||||
if (objectRegisterType.category == RegisterType.Category.UninitRef ||
|
||||
objectRegisterType.category == RegisterType.Category.UninitThis) {
|
||||
|
||||
if (!isInit) {
|
||||
throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
|
||||
"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 " +
|
||||
"does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
|
||||
methodClassDef.getClassType()));
|
||||
@ -2370,7 +2419,9 @@ public class MethodAnalyzer {
|
||||
RegisterType preInstructionRegisterType =
|
||||
analyzedInstruction.getMergedRegisterTypeFromPredecessors(i);
|
||||
|
||||
if (preInstructionRegisterType.category == RegisterType.Category.UninitRef) {
|
||||
if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
|
||||
preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
|
||||
|
||||
RegisterType registerType = null;
|
||||
if (preInstructionRegisterType == objectRegisterType) {
|
||||
registerType = analyzedInstruction.postRegisterMap[objectRegister];
|
||||
|
@ -15,8 +15,10 @@ public class RegisterType {
|
||||
public final ClassDef type;
|
||||
|
||||
private RegisterType(Category category, ClassDef type) {
|
||||
assert ((category == Category.Reference || category == Category.UninitRef) && type != null) ||
|
||||
((category != Category.Reference && category != Category.UninitRef) && type == null);
|
||||
assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
|
||||
type != null) ||
|
||||
((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
|
||||
type == null);
|
||||
|
||||
this.category = category;
|
||||
this.type = type;
|
||||
@ -65,9 +67,10 @@ public class RegisterType {
|
||||
DoubleLo,
|
||||
DoubleHi,
|
||||
//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,
|
||||
//the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
|
||||
//method is called
|
||||
UninitThis,
|
||||
Reference,
|
||||
//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
|
||||
@ -79,25 +82,26 @@ public class RegisterType {
|
||||
//could hold either type of value.
|
||||
protected static Category[][] mergeTable =
|
||||
{
|
||||
/* 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, Reference, Conflicted},
|
||||
/*Null*/ {Null, Null, Conflicted, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, 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},
|
||||
/*Boolean*/ {Boolean, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, 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},
|
||||
/*PosByte*/ {PosByte, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, 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},
|
||||
/*PosShort*/ {PosShort, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, 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},
|
||||
/*Integer*/ {Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, 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},
|
||||
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted},
|
||||
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted},
|
||||
/*UninitRef*/ {UninitRef, 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},
|
||||
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, 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, UninitThis, Reference, Conflicted},
|
||||
/*Null*/ {Null, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, 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, 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, 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, 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, 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, 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, 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, Conflicted},
|
||||
/*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, 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
|
||||
@ -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.
|
||||
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*/ {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},
|
||||
/*One*/ {false, false, true, 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},
|
||||
/*Byte*/ {false, false, false, false, true, false, true, true, false, 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},
|
||||
/*Short*/ {false, false, false, false, false, false, true, false, false, 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},
|
||||
/*Char*/ {false, false, false, false, false, false, false, false, true, 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},
|
||||
/*Float*/ {false, false, false, false, false, false, false, false, false, true, true, 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},
|
||||
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
|
||||
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
|
||||
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
|
||||
/*UninitRef*/ {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},
|
||||
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
|
||||
/* 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, 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, 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, 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, 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, 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, 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, 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, false},
|
||||
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
||||
/*UninitThis*/ {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, 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*/
|
||||
};
|
||||
@ -227,7 +232,7 @@ public class RegisterType {
|
||||
if (mergedCategory == Category.Reference) {
|
||||
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
||||
}
|
||||
if (mergedCategory == Category.UninitRef) {
|
||||
if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
|
||||
if (this.category == Category.Unknown) {
|
||||
return type;
|
||||
}
|
||||
|
@ -3,7 +3,22 @@ package org.jf.dexlib.Code.Analysis;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
|
||||
public class ValidationException extends ExceptionWithContext {
|
||||
private int codeAddress;
|
||||
|
||||
public ValidationException(int codeAddress, String errorMessage) {
|
||||
super(errorMessage);
|
||||
this.codeAddress = codeAddress;
|
||||
}
|
||||
|
||||
public ValidationException(String errorMessage) {
|
||||
super(errorMessage);
|
||||
}
|
||||
|
||||
public void setCodeAddress(int codeAddress) {
|
||||
this.codeAddress = codeAddress;
|
||||
}
|
||||
|
||||
public int getCodeAddress() {
|
||||
return codeAddress;
|
||||
}
|
||||
}
|
||||
|
@ -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_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),
|
||||
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_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t),
|
||||
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),
|
||||
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),
|
||||
ADD_FLOAT_2ADDR((byte)0xc6, "add-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 | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_FLOAT_2ADDR((byte)0xc8, "mul-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 | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_FLOAT_2ADDR((byte)0xca, "rem-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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
|
@ -147,6 +147,23 @@ public class MethodIdItem extends Item<MethodIdItem> {
|
||||
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
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user