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;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
|
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||||
import org.jf.dexlib.EncodedValue.EncodedValue;
|
import org.jf.dexlib.EncodedValue.EncodedValue;
|
||||||
import org.jf.dexlib.*;
|
import org.jf.dexlib.*;
|
||||||
import org.jf.dexlib.Code.Instruction;
|
import org.jf.dexlib.Code.Instruction;
|
||||||
@ -50,6 +51,8 @@ public class ClassDefinition {
|
|||||||
|
|
||||||
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
|
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
|
||||||
|
|
||||||
|
protected boolean validationErrors;
|
||||||
|
|
||||||
public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) {
|
public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) {
|
||||||
this.stg = stg;
|
this.stg = stg;
|
||||||
this.classDefItem = classDefItem;
|
this.classDefItem = classDefItem;
|
||||||
@ -75,6 +78,10 @@ public class ClassDefinition {
|
|||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hadValidationErrors() {
|
||||||
|
return validationErrors;
|
||||||
|
}
|
||||||
|
|
||||||
private void buildAnnotationMaps() {
|
private void buildAnnotationMaps() {
|
||||||
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
|
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
|
||||||
if (annotationDirectory == null) {
|
if (annotationDirectory == null) {
|
||||||
@ -270,6 +277,13 @@ public class ClassDefinition {
|
|||||||
MethodDefinition methodDefinition = new MethodDefinition(stg, method);
|
MethodDefinition methodDefinition = new MethodDefinition(stg, method);
|
||||||
|
|
||||||
methodTemplates.add(methodDefinition.createTemplate(annotationSet, parameterAnnotationList));
|
methodTemplates.add(methodDefinition.createTemplate(annotationSet, parameterAnnotationList));
|
||||||
|
|
||||||
|
ValidationException validationException = methodDefinition.getValidationException();
|
||||||
|
if (validationException != null) {
|
||||||
|
//System.err.println(validationException.toString());
|
||||||
|
validationException.printStackTrace(System.err);
|
||||||
|
this.validationErrors = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodTemplates;
|
return methodTemplates;
|
||||||
|
@ -34,6 +34,7 @@ import org.jf.dexlib.*;
|
|||||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||||
import org.jf.dexlib.Code.Analysis.RegisterType;
|
import org.jf.dexlib.Code.Analysis.RegisterType;
|
||||||
|
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
||||||
import org.jf.dexlib.Code.Instruction;
|
import org.jf.dexlib.Code.Instruction;
|
||||||
import org.jf.dexlib.Code.Opcode;
|
import org.jf.dexlib.Code.Opcode;
|
||||||
@ -75,11 +76,11 @@ public class MethodDefinition {
|
|||||||
AnalyzedInstruction instruction = instructions[i];
|
AnalyzedInstruction instruction = instructions[i];
|
||||||
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
|
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||||
packedSwitchMap.append(
|
packedSwitchMap.append(
|
||||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
|
||||||
currentCodeAddress);
|
currentCodeAddress);
|
||||||
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
|
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||||
sparseSwitchMap.append(
|
sparseSwitchMap.append(
|
||||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
|
||||||
currentCodeAddress);
|
currentCodeAddress);
|
||||||
}
|
}
|
||||||
instructionMap.append(currentCodeAddress, i);
|
instructionMap.append(currentCodeAddress, i);
|
||||||
@ -191,6 +192,14 @@ public class MethodDefinition {
|
|||||||
return labelCache;
|
return labelCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValidationException getValidationException() {
|
||||||
|
if (methodAnalyzer == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodAnalyzer.getValidationException();
|
||||||
|
}
|
||||||
|
|
||||||
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
|
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
|
||||||
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
|
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
|
||||||
|
|
||||||
@ -236,6 +245,12 @@ public class MethodDefinition {
|
|||||||
AnalyzedInstruction[] instructions;
|
AnalyzedInstruction[] instructions;
|
||||||
if (baksmali.verboseRegisterInfo) {
|
if (baksmali.verboseRegisterInfo) {
|
||||||
instructions = methodAnalyzer.analyze();
|
instructions = methodAnalyzer.analyze();
|
||||||
|
|
||||||
|
ValidationException validationException = methodAnalyzer.getValidationException();
|
||||||
|
if (validationException != null) {
|
||||||
|
methodItems.add(new CommentMethodItem(stg, validationException.getMessage(),
|
||||||
|
validationException.getCodeAddress(), Integer.MIN_VALUE));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
instructions = methodAnalyzer.makeInstructionArray();
|
instructions = methodAnalyzer.makeInstructionArray();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public class baksmali {
|
|||||||
baksmali.bootClassPath = bootClassPath;
|
baksmali.bootClassPath = bootClassPath;
|
||||||
|
|
||||||
if (verboseRegisterInfo) {
|
if (verboseRegisterInfo) {
|
||||||
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
|
ClassPath.InitializeClassPath(bootClassPath==null?null:bootClassPath.split(":"), dexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deodexerant != null) {
|
if (deodexerant != null) {
|
||||||
@ -167,6 +167,11 @@ public class baksmali {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: GROT
|
||||||
|
if (classDefinition.hadValidationErrors()) {
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ public class main {
|
|||||||
|
|
||||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
||||||
.withDescription("the bootclasspath jars to use, for analysis")
|
.withDescription("the bootclasspath jars to use, for analysis")
|
||||||
.hasArg()
|
.hasOptionalArg()
|
||||||
.withArgName("BOOTCLASSPATH")
|
.withArgName("BOOTCLASSPATH")
|
||||||
.create("c");
|
.create("c");
|
||||||
|
|
||||||
|
@ -147,12 +147,23 @@ public class AnalyzedInstruction {
|
|||||||
//object register. If the uninitialized reference has been copied to other registers, they will be initialized
|
//object register. If the uninitialized reference has been copied to other registers, they will be initialized
|
||||||
//as well, so we need to check for that too
|
//as well, so we need to check for that too
|
||||||
if (isInvokeInit()) {
|
if (isInvokeInit()) {
|
||||||
int destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
|
int destinationRegister;
|
||||||
|
if (instruction instanceof FiveRegisterInstruction) {
|
||||||
|
destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
|
||||||
|
} else {
|
||||||
|
assert instruction instanceof RegisterRangeInstruction;
|
||||||
|
RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
|
||||||
|
assert rangeInstruction.getRegCount() > 0;
|
||||||
|
destinationRegister = rangeInstruction.getStartRegister();
|
||||||
|
}
|
||||||
|
|
||||||
if (registerNumber == destinationRegister) {
|
if (registerNumber == destinationRegister) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(destinationRegister);
|
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(registerNumber);
|
||||||
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef) {
|
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
|
||||||
|
preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//check if the uninit ref has been copied to another register
|
//check if the uninit ref has been copied to another register
|
||||||
|
@ -31,12 +31,10 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initClassPath(String[] bootClassPath, DexFile dexFile) {
|
private void initClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||||
if (bootClassPath == null || bootClassPath.length == 0) {
|
if (bootClassPath != null) {
|
||||||
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
|
for (String bootClassPathEntry: bootClassPath) {
|
||||||
}
|
loadBootClassPath(bootClassPathEntry);
|
||||||
|
}
|
||||||
for (String bootClassPathEntry: bootClassPath) {
|
|
||||||
loadBootClassPath(bootClassPathEntry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDexFile(dexFile);
|
loadDexFile(dexFile);
|
||||||
@ -267,6 +265,46 @@ public class ClassPath {
|
|||||||
public int getArrayDimensions() {
|
public int getArrayDimensions() {
|
||||||
return arrayDimensions;
|
return arrayDimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean extendsClass(ClassDef superclassDef) {
|
||||||
|
if (!(superclassDef instanceof ArrayClassDef)) {
|
||||||
|
if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) {
|
||||||
|
return true;
|
||||||
|
} else if (superclassDef.isInterface) {
|
||||||
|
return this.implementsInterface(superclassDef);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef;
|
||||||
|
if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) {
|
||||||
|
ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
|
||||||
|
|
||||||
|
if (baseElementClass.isInterface) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass());
|
||||||
|
} else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) {
|
||||||
|
ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
|
||||||
|
if (baseElementClass.isInterface) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean implementsInterface(ClassDef interfaceDef) {
|
||||||
|
return interfaceDef.classType.equals("Ljava/lang/Cloneable;") ||
|
||||||
|
interfaceDef.classType.equals("Ljava/io/Serializable;");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PrimitiveClassDef extends ClassDef {
|
public static class PrimitiveClassDef extends ClassDef {
|
||||||
@ -524,7 +562,7 @@ public class ClassPath {
|
|||||||
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
|
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
|
||||||
if (virtualMethods != null) {
|
if (virtualMethods != null) {
|
||||||
for (EncodedMethod virtualMethod: virtualMethods) {
|
for (EncodedMethod virtualMethod: virtualMethods) {
|
||||||
String methodString = virtualMethod.method.getMethodString();
|
String methodString = virtualMethod.method.getVirtualMethodString();
|
||||||
if (tempVirtualMethodLookup.get(methodString) == null) {
|
if (tempVirtualMethodLookup.get(methodString) == null) {
|
||||||
virtualMethodList.add(methodString);
|
virtualMethodList.add(methodString);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
private boolean analyzed = false;
|
private boolean analyzed = false;
|
||||||
|
|
||||||
private BitSet instructionsToVerify;
|
private BitSet verifiedInstructions;
|
||||||
|
|
||||||
|
private ValidationException validationException = null;
|
||||||
|
|
||||||
//This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
|
//This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
|
||||||
//register types for this instruction to the parameter types, in order to have them propagate to all of its
|
//register types for this instruction to the parameter types, in order to have them propagate to all of its
|
||||||
@ -58,7 +60,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
buildInstructionList();
|
buildInstructionList();
|
||||||
|
|
||||||
instructionsToVerify = new BitSet(instructions.size());
|
verifiedInstructions = new BitSet(instructions.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnalyzedInstruction[] analyze() {
|
public AnalyzedInstruction[] analyze() {
|
||||||
@ -88,7 +90,7 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
|
setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
|
||||||
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
|
RegisterType.getRegisterType(RegisterType.Category.UninitThis,
|
||||||
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
||||||
} else {
|
} else {
|
||||||
if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||||
@ -111,17 +113,40 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BitSet analyzedInstructions = new BitSet(instructionsToVerify.size());
|
BitSet instructionsToAnalyze = new BitSet(verifiedInstructions.size());
|
||||||
|
|
||||||
//make sure all of the "first instructions" are marked for processing
|
//make sure all of the "first instructions" are marked for processing
|
||||||
for (AnalyzedInstruction successor: startOfMethod.successors) {
|
for (AnalyzedInstruction successor: startOfMethod.successors) {
|
||||||
instructionsToVerify.set(successor.instructionIndex);
|
instructionsToAnalyze.set(successor.instructionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!instructionsToVerify.isEmpty()) {
|
while (!instructionsToAnalyze.isEmpty()) {
|
||||||
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
|
for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
|
||||||
instructionsToVerify.clear(i);
|
instructionsToAnalyze.clear(i);
|
||||||
analyzeInstruction(instructions.valueAt(i));
|
if (verifiedInstructions.get(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AnalyzedInstruction instructionToVerify = instructions.valueAt(i);
|
||||||
|
try {
|
||||||
|
analyzeInstruction(instructionToVerify);
|
||||||
|
} catch (ValidationException ex) {
|
||||||
|
this.validationException = ex;
|
||||||
|
int codeAddress = getInstructionAddress(instructionToVerify);
|
||||||
|
ex.setCodeAddress(codeAddress);
|
||||||
|
ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name));
|
||||||
|
ex.addContext(String.format("CodeAddress: %d", codeAddress));
|
||||||
|
ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifiedInstructions.set(instructionToVerify.getInstructionIndex());
|
||||||
|
|
||||||
|
for (AnalyzedInstruction successor: instructionToVerify.successors) {
|
||||||
|
instructionsToAnalyze.set(successor.getInstructionIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validationException != null) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +191,10 @@ public class MethodAnalyzer {
|
|||||||
return instructionArray;
|
return instructionArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValidationException getValidationException() {
|
||||||
|
return validationException;
|
||||||
|
}
|
||||||
|
|
||||||
private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
|
private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
|
||||||
assert typeListItem != null;
|
assert typeListItem != null;
|
||||||
assert parameterRegisterCount == typeListItem.getRegisterCount();
|
assert parameterRegisterCount == typeListItem.getRegisterCount();
|
||||||
@ -178,7 +207,7 @@ public class MethodAnalyzer {
|
|||||||
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
|
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
|
||||||
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
|
registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
|
||||||
} else {
|
} else {
|
||||||
registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type);
|
registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +279,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
||||||
changedInstructions.set(successor.instructionIndex);
|
changedInstructions.set(successor.instructionIndex);
|
||||||
instructionsToVerify.set(successor.instructionIndex);
|
verifiedInstructions.clear(successor.instructionIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,6 +317,7 @@ public class MethodAnalyzer {
|
|||||||
for (int i=0; i<instructions.size(); i++) {
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||||
|
currentCodeAddress = getInstructionAddress(instruction);
|
||||||
|
|
||||||
//check if we have gone past the end of the current try
|
//check if we have gone past the end of the current try
|
||||||
if (currentTry != null) {
|
if (currentTry != null) {
|
||||||
@ -328,11 +358,16 @@ public class MethodAnalyzer {
|
|||||||
int instructionCodeAddress = getInstructionAddress(instruction);
|
int instructionCodeAddress = getInstructionAddress(instruction);
|
||||||
|
|
||||||
if (instruction.instruction.opcode.canContinue()) {
|
if (instruction.instruction.opcode.canContinue()) {
|
||||||
if (i == instructions.size() - 1) {
|
if (instruction.instruction.opcode != Opcode.NOP ||
|
||||||
throw new ValidationException("Execution can continue past the last instruction");
|
!instruction.instruction.getFormat().variableSizeFormat) {
|
||||||
|
|
||||||
|
if (i == instructions.size() - 1) {
|
||||||
|
throw new ValidationException("Execution can continue past the last instruction");
|
||||||
|
}
|
||||||
|
|
||||||
|
AnalyzedInstruction nextInstruction = instructions.valueAt(i+1);
|
||||||
|
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
|
||||||
}
|
}
|
||||||
AnalyzedInstruction nextInstruction = instructions.valueAt(i+1);
|
|
||||||
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instruction.instruction instanceof OffsetInstruction) {
|
if (instruction.instruction instanceof OffsetInstruction) {
|
||||||
@ -387,7 +422,7 @@ public class MethodAnalyzer {
|
|||||||
if (exceptionHandlersForSuccessor != null) {
|
if (exceptionHandlersForSuccessor != null) {
|
||||||
//the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
|
//the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
|
||||||
//can throw an exception
|
//can throw an exception
|
||||||
assert predecessor.instruction.opcode.canThrow();
|
assert successor.instruction.opcode.canThrow();
|
||||||
|
|
||||||
for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
|
for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
|
||||||
addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true);
|
addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true);
|
||||||
@ -778,7 +813,7 @@ public class MethodAnalyzer {
|
|||||||
case XOR_INT:
|
case XOR_INT:
|
||||||
handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
|
handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
|
||||||
RegisterType.Category.Integer, true);
|
RegisterType.Category.Integer, true);
|
||||||
|
return;
|
||||||
case ADD_LONG:
|
case ADD_LONG:
|
||||||
case SUB_LONG:
|
case SUB_LONG:
|
||||||
case MUL_LONG:
|
case MUL_LONG:
|
||||||
@ -928,9 +963,15 @@ public class MethodAnalyzer {
|
|||||||
RegisterType.Category.Null,
|
RegisterType.Category.Null,
|
||||||
RegisterType.Category.Reference);
|
RegisterType.Category.Reference);
|
||||||
|
|
||||||
|
private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of(
|
||||||
|
RegisterType.Category.Null,
|
||||||
|
RegisterType.Category.UninitThis,
|
||||||
|
RegisterType.Category.Reference);
|
||||||
|
|
||||||
private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
|
private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
|
||||||
RegisterType.Category.Null,
|
RegisterType.Category.Null,
|
||||||
RegisterType.Category.UninitRef,
|
RegisterType.Category.UninitRef,
|
||||||
|
RegisterType.Category.UninitThis,
|
||||||
RegisterType.Category.Reference);
|
RegisterType.Category.Reference);
|
||||||
|
|
||||||
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
||||||
@ -1036,7 +1077,8 @@ public class MethodAnalyzer {
|
|||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
|
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
|
//TODO: GROT
|
||||||
|
/*private void checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
|
||||||
assert this.isInstanceConstructor();
|
assert this.isInstanceConstructor();
|
||||||
|
|
||||||
//if we're in an instance constructor (an <init> method), then the superclass <init> must have been called.
|
//if we're in an instance constructor (an <init> method), then the superclass <init> must have been called.
|
||||||
@ -1053,12 +1095,12 @@ public class MethodAnalyzer {
|
|||||||
//TODO: GROT
|
//TODO: GROT
|
||||||
//assert thisRegisterType.category == RegisterType.Category.Reference;
|
//assert thisRegisterType.category == RegisterType.Category.Reference;
|
||||||
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
||||||
if (this.isInstanceConstructor()) {
|
/*if (this.isInstanceConstructor()) {
|
||||||
checkConstructorReturn(analyzedInstruction);
|
checkConstructorReturn(analyzedInstruction);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
|
TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
|
||||||
if (returnType.getTypeDescriptor().charAt(0) != 'V') {
|
if (returnType.getTypeDescriptor().charAt(0) != 'V') {
|
||||||
@ -1069,9 +1111,9 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
|
private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
|
||||||
if (this.isInstanceConstructor()) {
|
/*if (this.isInstanceConstructor()) {
|
||||||
checkConstructorReturn(analyzedInstruction);
|
checkConstructorReturn(analyzedInstruction);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
|
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
|
||||||
int returnRegister = instruction.getRegisterA();
|
int returnRegister = instruction.getRegisterA();
|
||||||
@ -1093,11 +1135,14 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
if (validCategories == ReferenceCategories) {
|
if (validCategories == ReferenceCategories) {
|
||||||
if (methodReturnRegisterType.type.isInterface()) {
|
if (methodReturnRegisterType.type.isInterface()) {
|
||||||
if (!returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
|
if (returnRegisterType.category != RegisterType.Category.Null &&
|
||||||
|
!returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
|
||||||
//TODO: how to handle warnings?
|
//TODO: how to handle warnings?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
|
if (returnRegisterType.category == RegisterType.Category.Reference &&
|
||||||
|
!returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
|
||||||
|
|
||||||
throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
|
throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
|
||||||
"compatible with the method's return type %s", returnRegister,
|
"compatible with the method's return type %s", returnRegister,
|
||||||
returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
|
returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
|
||||||
@ -1147,9 +1192,11 @@ public class MethodAnalyzer {
|
|||||||
Item item = instruction.getReferencedItem();
|
Item item = instruction.getReferencedItem();
|
||||||
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
|
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
|
||||||
|
|
||||||
//make sure the referenced class is resolvable
|
|
||||||
//TODO: need to check class access
|
//TODO: need to check class access
|
||||||
ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
|
//make sure the referenced class is resolvable
|
||||||
|
ClassPath.getClassDef((TypeIdItem)item);
|
||||||
|
|
||||||
|
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMonitor(AnalyzedInstruction analyzedInstruction) {
|
private void handleMonitor(AnalyzedInstruction analyzedInstruction) {
|
||||||
@ -1224,10 +1271,9 @@ public class MethodAnalyzer {
|
|||||||
throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
|
throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
|
||||||
arrayRegisterType.type.getClassType()));
|
arrayRegisterType.type.getClassType()));
|
||||||
}
|
}
|
||||||
|
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
|
|
||||||
|
|
||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
|
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
|
||||||
RegisterType.getRegisterType(RegisterType.Category.Integer, null));
|
RegisterType.getRegisterType(RegisterType.Category.Integer, null));
|
||||||
}
|
}
|
||||||
@ -1255,6 +1301,8 @@ public class MethodAnalyzer {
|
|||||||
"that was created by this new-instance instruction.", i));
|
"that was created by this new-instance instruction.", i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Item item = instruction.getReferencedItem();
|
Item item = instruction.getReferencedItem();
|
||||||
@ -1327,7 +1375,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
public boolean moveNext() {
|
public boolean moveNext() {
|
||||||
currentRegister++;
|
currentRegister++;
|
||||||
return pastEnd();
|
return !pastEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
@ -1355,7 +1403,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
public boolean moveNext() {
|
public boolean moveNext() {
|
||||||
currentRegister++;
|
currentRegister++;
|
||||||
return pastEnd();
|
return !pastEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
@ -1405,8 +1453,6 @@ public class MethodAnalyzer {
|
|||||||
arrayType.type.getClassType());
|
arrayType.type.getClassType());
|
||||||
}
|
}
|
||||||
} while (registerIterator.moveNext());
|
} while (registerIterator.moveNext());
|
||||||
|
|
||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
|
private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
|
||||||
@ -1619,7 +1665,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
RegisterType arrayBaseType =
|
RegisterType arrayBaseType =
|
||||||
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
|
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
|
||||||
if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
|
if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
|
||||||
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
|
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
|
||||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||||
arrayRegisterType.type.getClassType()));
|
arrayRegisterType.type.getClassType()));
|
||||||
@ -1753,7 +1799,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
RegisterType arrayBaseType =
|
RegisterType arrayBaseType =
|
||||||
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
|
RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
|
||||||
if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
|
if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
|
||||||
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
|
throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
|
||||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||||
arrayRegisterType.type.getClassType()));
|
arrayRegisterType.type.getClassType()));
|
||||||
@ -1842,10 +1888,9 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -1872,10 +1917,9 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -1901,10 +1945,9 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -1931,7 +1974,7 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
|
RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
|
||||||
assert sourceRegisterType != null;
|
assert sourceRegisterType != null;
|
||||||
@ -1952,7 +1995,6 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -1976,12 +2018,11 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
|
getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -2005,13 +2046,12 @@ public class MethodAnalyzer {
|
|||||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||||
|
|
||||||
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
||||||
ReferenceCategories);
|
ReferenceOrUninitThisCategories);
|
||||||
|
|
||||||
RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
|
RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
|
||||||
ReferenceCategories);
|
ReferenceCategories);
|
||||||
|
|
||||||
//TODO: check access
|
//TODO: check access
|
||||||
//TODO: allow an uninitialized "this" reference, if the current method is an <init> method
|
|
||||||
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
|
||||||
assert referencedItem instanceof FieldIdItem;
|
assert referencedItem instanceof FieldIdItem;
|
||||||
FieldIdItem field = (FieldIdItem)referencedItem;
|
FieldIdItem field = (FieldIdItem)referencedItem;
|
||||||
@ -2148,8 +2188,6 @@ public class MethodAnalyzer {
|
|||||||
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
"for the instruction.", analyzedInstruction.instruction.opcode.name,
|
||||||
field.getFieldString()));
|
field.getFieldString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSputObject(AnalyzedInstruction analyzedInstruction) {
|
private void handleSputObject(AnalyzedInstruction analyzedInstruction) {
|
||||||
@ -2235,13 +2273,21 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((invokeType & INVOKE_SUPER) != 0) {
|
if ((invokeType & INVOKE_SUPER) != 0) {
|
||||||
if (methodClassDef.getSuperclass() == null) {
|
ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||||
|
if (currentMethodClassDef.getSuperclass() == null) {
|
||||||
throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
|
throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
|
||||||
methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
|
methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
|
||||||
methodClassDef.getSuperclass().getClassType()));
|
methodClassDef.getSuperclass().getClassType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!methodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getMethodString())) {
|
if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) {
|
||||||
|
throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " +
|
||||||
|
"of the current class %s", methodIdItem.getMethodString(),
|
||||||
|
analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(),
|
||||||
|
encodedMethod.method.getContainingClass().getTypeDescriptor()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getVirtualMethodString())) {
|
||||||
throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
|
throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
|
||||||
"no such method", methodIdItem.getMethodString(),
|
"no such method", methodIdItem.getMethodString(),
|
||||||
analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
|
analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
|
||||||
@ -2276,7 +2322,9 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
|
objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
|
||||||
assert objectRegisterType != null;
|
assert objectRegisterType != null;
|
||||||
if (objectRegisterType.category == RegisterType.Category.UninitRef) {
|
if (objectRegisterType.category == RegisterType.Category.UninitRef ||
|
||||||
|
objectRegisterType.category == RegisterType.Category.UninitThis) {
|
||||||
|
|
||||||
if (!isInit) {
|
if (!isInit) {
|
||||||
throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
|
throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
|
||||||
"reference type %s", methodIdItem.getMethodString(),
|
"reference type %s", methodIdItem.getMethodString(),
|
||||||
@ -2308,8 +2356,9 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null &&
|
||||||
|
!objectRegisterType.type.extendsClass(methodClassDef)) {
|
||||||
|
|
||||||
if ((invokeType & INVOKE_INTERFACE) == 0 && !objectRegisterType.type.extendsClass(methodClassDef)) {
|
|
||||||
throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
|
throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
|
||||||
"does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
|
"does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
|
||||||
methodClassDef.getClassType()));
|
methodClassDef.getClassType()));
|
||||||
@ -2370,7 +2419,9 @@ public class MethodAnalyzer {
|
|||||||
RegisterType preInstructionRegisterType =
|
RegisterType preInstructionRegisterType =
|
||||||
analyzedInstruction.getMergedRegisterTypeFromPredecessors(i);
|
analyzedInstruction.getMergedRegisterTypeFromPredecessors(i);
|
||||||
|
|
||||||
if (preInstructionRegisterType.category == RegisterType.Category.UninitRef) {
|
if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
|
||||||
|
preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
|
||||||
|
|
||||||
RegisterType registerType = null;
|
RegisterType registerType = null;
|
||||||
if (preInstructionRegisterType == objectRegisterType) {
|
if (preInstructionRegisterType == objectRegisterType) {
|
||||||
registerType = analyzedInstruction.postRegisterMap[objectRegister];
|
registerType = analyzedInstruction.postRegisterMap[objectRegister];
|
||||||
|
@ -15,8 +15,10 @@ public class RegisterType {
|
|||||||
public final ClassDef type;
|
public final ClassDef type;
|
||||||
|
|
||||||
private RegisterType(Category category, ClassDef type) {
|
private RegisterType(Category category, ClassDef type) {
|
||||||
assert ((category == Category.Reference || category == Category.UninitRef) && type != null) ||
|
assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
|
||||||
((category != Category.Reference && category != Category.UninitRef) && type == null);
|
type != null) ||
|
||||||
|
((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
|
||||||
|
type == null);
|
||||||
|
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -65,9 +67,10 @@ public class RegisterType {
|
|||||||
DoubleLo,
|
DoubleLo,
|
||||||
DoubleHi,
|
DoubleHi,
|
||||||
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
|
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
|
||||||
//it is also used for the "this" register inside an <init> method, before the superclass' <init> method is
|
|
||||||
//called
|
|
||||||
UninitRef,
|
UninitRef,
|
||||||
|
//the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
|
||||||
|
//method is called
|
||||||
|
UninitThis,
|
||||||
Reference,
|
Reference,
|
||||||
//This is used when there are multiple incoming execution paths that have incompatible register types. For
|
//This is used when there are multiple incoming execution paths that have incompatible register types. For
|
||||||
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another
|
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another
|
||||||
@ -79,25 +82,26 @@ public class RegisterType {
|
|||||||
//could hold either type of value.
|
//could hold either type of value.
|
||||||
protected static Category[][] mergeTable =
|
protected static Category[][] mergeTable =
|
||||||
{
|
{
|
||||||
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted*/
|
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/
|
||||||
/*Unknown*/ {Unknown, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, Reference, Conflicted},
|
/*Unknown*/ {Unknown, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, Reference, Conflicted},
|
||||||
/*Null*/ {Null, Null, Conflicted, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
|
/*Null*/ {Null, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
|
||||||
/*One*/ {One, Conflicted, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*One*/ {One, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Boolean*/ {Boolean, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Boolean*/ {Boolean, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Byte*/ {Byte, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Byte*/ {Byte, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*PosByte*/ {PosByte, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*PosByte*/ {PosByte, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Short*/ {Short, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Short*/ {Short, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*PosShort*/ {PosShort, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*PosShort*/ {PosShort, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Char*/ {Char, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Char*/ {Char, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Integer*/ {Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Integer*/ {Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Float*/ {Float, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*Float*/ {Float, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted},
|
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted},
|
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
/*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
|
||||||
/*Reference*/ {Reference, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
|
/*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted},
|
||||||
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
|
/*Reference*/ {Reference, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
|
||||||
|
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
|
||||||
};
|
};
|
||||||
|
|
||||||
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
|
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
|
||||||
@ -107,25 +111,26 @@ public class RegisterType {
|
|||||||
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
|
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
|
||||||
protected static boolean[][] assigmentTable =
|
protected static boolean[][] assigmentTable =
|
||||||
{
|
{
|
||||||
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted |slot type*/
|
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/
|
||||||
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
||||||
/*Null*/ {false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, true, false},
|
/*Null*/ {false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false},
|
||||||
/*One*/ {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
|
/*One*/ {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Boolean*/ {false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
|
/*Boolean*/ {false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Byte*/ {false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false},
|
/*Byte*/ {false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*PosByte*/ {false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
|
/*PosByte*/ {false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Short*/ {false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false},
|
/*Short*/ {false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*PosShort*/ {false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false},
|
/*PosShort*/ {false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Char*/ {false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false},
|
/*Char*/ {false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Integer*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false},
|
/*Integer*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*Float*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false},
|
/*Float*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
|
||||||
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
|
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
|
||||||
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
|
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
|
||||||
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
|
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
|
||||||
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
|
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
|
||||||
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
||||||
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
|
/*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
|
||||||
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
|
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
|
||||||
|
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
|
||||||
/*----------*/
|
/*----------*/
|
||||||
/*value type*/
|
/*value type*/
|
||||||
};
|
};
|
||||||
@ -227,7 +232,7 @@ public class RegisterType {
|
|||||||
if (mergedCategory == Category.Reference) {
|
if (mergedCategory == Category.Reference) {
|
||||||
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
||||||
}
|
}
|
||||||
if (mergedCategory == Category.UninitRef) {
|
if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
|
||||||
if (this.category == Category.Unknown) {
|
if (this.category == Category.Unknown) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,22 @@ package org.jf.dexlib.Code.Analysis;
|
|||||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||||
|
|
||||||
public class ValidationException extends ExceptionWithContext {
|
public class ValidationException extends ExceptionWithContext {
|
||||||
|
private int codeAddress;
|
||||||
|
|
||||||
|
public ValidationException(int codeAddress, String errorMessage) {
|
||||||
|
super(errorMessage);
|
||||||
|
this.codeAddress = codeAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public ValidationException(String errorMessage) {
|
public ValidationException(String errorMessage) {
|
||||||
super(errorMessage);
|
super(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCodeAddress(int codeAddress) {
|
||||||
|
this.codeAddress = codeAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCodeAddress() {
|
||||||
|
return codeAddress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ public enum Opcode
|
|||||||
FILLED_NEW_ARRAY((byte)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
FILLED_NEW_ARRAY((byte)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||||
FILLED_NEW_ARRAY_RANGE((byte)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
FILLED_NEW_ARRAY_RANGE((byte)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||||
FILL_ARRAY_DATA((byte)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
|
FILL_ARRAY_DATA((byte)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||||
THROW((byte)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
THROW((byte)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW),
|
||||||
GOTO((byte)0x28, "goto", ReferenceType.none, Format.Format10t),
|
GOTO((byte)0x28, "goto", ReferenceType.none, Format.Format10t),
|
||||||
GOTO_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t),
|
GOTO_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t),
|
||||||
GOTO_32((byte)0x2a, "goto/32", ReferenceType.none, Format.Format30t),
|
GOTO_32((byte)0x2a, "goto/32", ReferenceType.none, Format.Format30t),
|
||||||
@ -223,11 +223,11 @@ public enum Opcode
|
|||||||
SHL_LONG_2ADDR((byte)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
SHL_LONG_2ADDR((byte)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
SHR_LONG_2ADDR((byte)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
SHR_LONG_2ADDR((byte)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
USHR_LONG_2ADDR((byte)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
USHR_LONG_2ADDR((byte)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
ADD_FLOAT_2ADDR((byte)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
ADD_FLOAT_2ADDR((byte)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||||
SUB_FLOAT_2ADDR((byte)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
SUB_FLOAT_2ADDR((byte)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||||
MUL_FLOAT_2ADDR((byte)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
MUL_FLOAT_2ADDR((byte)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||||
DIV_FLOAT_2ADDR((byte)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
DIV_FLOAT_2ADDR((byte)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||||
REM_FLOAT_2ADDR((byte)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
REM_FLOAT_2ADDR((byte)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||||
ADD_DOUBLE_2ADDR((byte)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
ADD_DOUBLE_2ADDR((byte)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
SUB_DOUBLE_2ADDR((byte)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
SUB_DOUBLE_2ADDR((byte)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
MUL_DOUBLE_2ADDR((byte)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
MUL_DOUBLE_2ADDR((byte)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||||
|
@ -147,6 +147,23 @@ public class MethodIdItem extends Item<MethodIdItem> {
|
|||||||
return cachedMethodString;
|
return cachedMethodString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String cachedVirtualMethodString = null;
|
||||||
|
/**
|
||||||
|
* @return a string formatted like methodName(TTTT..)R
|
||||||
|
*/
|
||||||
|
public String getVirtualMethodString() {
|
||||||
|
if (cachedVirtualMethodString == null) {
|
||||||
|
String methodName = this.methodName.getStringValue();
|
||||||
|
String prototypeString = methodPrototype.getPrototypeString();
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length());
|
||||||
|
sb.append(methodName);
|
||||||
|
sb.append(prototypeString);
|
||||||
|
cachedVirtualMethodString = sb.toString();
|
||||||
|
}
|
||||||
|
return cachedVirtualMethodString;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the method prototype
|
* @return the method prototype
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user