From c9be5e13034da9827b5598a6257376164745b827 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Thu, 11 Feb 2010 00:58:02 +0000 Subject: [PATCH] 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 --- .../jf/baksmali/Adaptors/ClassDefinition.java | 14 ++ .../baksmali/Adaptors/MethodDefinition.java | 19 ++- .../main/java/org/jf/baksmali/baksmali.java | 7 +- .../src/main/java/org/jf/baksmali/main.java | 2 +- .../Code/Analysis/AnalyzedInstruction.java | 17 +- .../jf/dexlib/Code/Analysis/ClassPath.java | 52 +++++- .../dexlib/Code/Analysis/MethodAnalyzer.java | 161 ++++++++++++------ .../jf/dexlib/Code/Analysis/RegisterType.java | 91 +++++----- .../Code/Analysis/ValidationException.java | 15 ++ .../main/java/org/jf/dexlib/Code/Opcode.java | 12 +- .../main/java/org/jf/dexlib/MethodIdItem.java | 17 ++ 11 files changed, 289 insertions(+), 118 deletions(-) diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 683f0b4e..beb0a9c0 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -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 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; diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 776dc14e..75824300 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -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(); } diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index a7b2afee..ff590256 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -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); + } } } } diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java index 9d823155..49a49e48 100644 --- a/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/baksmali/src/main/java/org/jf/baksmali/main.java @@ -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"); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java index 5049baa9..7dd57cd4 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java @@ -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 diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java index 83986056..dc3d7f69 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java @@ -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); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java index 879bbe84..89f43d44 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -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("")) { @@ -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 ReferenceOrUninitThisCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.UninitThis, + RegisterType.Category.Reference); + private static final EnumSet ReferenceOrUninitCategories = EnumSet.of( RegisterType.Category.Null, RegisterType.Category.UninitRef, + RegisterType.Category.UninitThis, RegisterType.Category.Reference); private static final EnumSet 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 method), then the superclass 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 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 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 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 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 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 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- 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]; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java index 31100c1c..712d1049 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java @@ -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 is called - //it is also used for the "this" register inside an method, before the superclass' method is - //called UninitRef, + //the UninitThis category is used the "this" register inside an method, before the superclass' + //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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java index b602f7ae..8b9ec44c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java @@ -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; + } } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java index 92111e53..d0505e80 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java @@ -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), diff --git a/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java index 61882f0f..ecdc66ee 100644 --- a/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java @@ -147,6 +147,23 @@ public class MethodIdItem extends Item { 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 */