mirror of
https://github.com/revanced/smali.git
synced 2025-05-05 00:54:25 +02:00
Preliminary implementation of "verbose register info" functionality
git-svn-id: https://smali.googlecode.com/svn/trunk@620 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
caea37a7d2
commit
7e24a9f010
@ -0,0 +1,25 @@
|
|||||||
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
|
import org.antlr.stringtemplate.StringTemplate;
|
||||||
|
import org.antlr.stringtemplate.StringTemplateGroup;
|
||||||
|
|
||||||
|
public class CommentMethodItem extends MethodItem {
|
||||||
|
private final StringTemplate template;
|
||||||
|
private final int sortOrder;
|
||||||
|
|
||||||
|
public CommentMethodItem(StringTemplateGroup stg, String comment, int codeAddress, int sortOrder) {
|
||||||
|
super(codeAddress);
|
||||||
|
template = stg.getInstanceOf("Comment");
|
||||||
|
template.setAttribute("Comment", comment);
|
||||||
|
this.sortOrder = sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSortOrder() {
|
||||||
|
return sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return template.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,9 @@ package org.jf.baksmali.Adaptors;
|
|||||||
import org.jf.baksmali.Adaptors.Format.*;
|
import org.jf.baksmali.Adaptors.Format.*;
|
||||||
import org.jf.baksmali.baksmali;
|
import org.jf.baksmali.baksmali;
|
||||||
import org.jf.dexlib.*;
|
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.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;
|
||||||
@ -45,6 +48,7 @@ import java.util.*;
|
|||||||
public class MethodDefinition {
|
public class MethodDefinition {
|
||||||
private final StringTemplateGroup stg;
|
private final StringTemplateGroup stg;
|
||||||
private final ClassDataItem.EncodedMethod encodedMethod;
|
private final ClassDataItem.EncodedMethod encodedMethod;
|
||||||
|
private final MethodAnalyzer methodAnalyzer;
|
||||||
|
|
||||||
private final LabelCache labelCache = new LabelCache();
|
private final LabelCache labelCache = new LabelCache();
|
||||||
|
|
||||||
@ -59,7 +63,8 @@ public class MethodDefinition {
|
|||||||
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
||||||
|
|
||||||
if (encodedMethod.codeItem != null) {
|
if (encodedMethod.codeItem != null) {
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
methodAnalyzer = new MethodAnalyzer(encodedMethod);
|
||||||
|
AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
|
||||||
|
|
||||||
packedSwitchMap = new SparseIntArray(1);
|
packedSwitchMap = new SparseIntArray(1);
|
||||||
sparseSwitchMap = new SparseIntArray(1);
|
sparseSwitchMap = new SparseIntArray(1);
|
||||||
@ -67,23 +72,24 @@ public class MethodDefinition {
|
|||||||
|
|
||||||
int currentCodeAddress = 0;
|
int currentCodeAddress = 0;
|
||||||
for (int i=0; i<instructions.length; i++) {
|
for (int i=0; i<instructions.length; i++) {
|
||||||
Instruction instruction = instructions[i];
|
AnalyzedInstruction instruction = instructions[i];
|
||||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||||
packedSwitchMap.append(
|
packedSwitchMap.append(
|
||||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||||
currentCodeAddress);
|
currentCodeAddress);
|
||||||
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
|
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||||
sparseSwitchMap.append(
|
sparseSwitchMap.append(
|
||||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||||
currentCodeAddress);
|
currentCodeAddress);
|
||||||
}
|
}
|
||||||
instructionMap.append(currentCodeAddress, i);
|
instructionMap.append(currentCodeAddress, i);
|
||||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
packedSwitchMap = null;
|
packedSwitchMap = null;
|
||||||
sparseSwitchMap = null;
|
sparseSwitchMap = null;
|
||||||
instructionMap = null;
|
instructionMap = null;
|
||||||
|
methodAnalyzer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,20 +233,35 @@ public class MethodDefinition {
|
|||||||
return methodItems;
|
return methodItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
AnalyzedInstruction[] instructions;
|
||||||
|
if (baksmali.verboseRegisterInfo) {
|
||||||
|
instructions = methodAnalyzer.analyze();
|
||||||
|
} else {
|
||||||
|
instructions = methodAnalyzer.makeInstructionArray();
|
||||||
|
}
|
||||||
|
|
||||||
int currentCodeAddress = 0;
|
int currentCodeAddress = 0;
|
||||||
for (int i=0; i<instructions.length; i++) {
|
for (int i=0; i<instructions.length; i++) {
|
||||||
Instruction instruction = instructions[i];
|
AnalyzedInstruction instruction = instructions[i];
|
||||||
|
|
||||||
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||||
encodedMethod.codeItem, currentCodeAddress, stg, instruction));
|
encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
|
||||||
|
|
||||||
if (i != instructions.length - 1) {
|
if (i != instructions.length - 1) {
|
||||||
methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
|
methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
if (baksmali.verboseRegisterInfo) {
|
||||||
|
if (instruction.getPredecessorCount() > 1 || i == 0) {
|
||||||
|
methodItems.add(new CommentMethodItem(stg, getPreInstructionRegisterString(instruction),
|
||||||
|
currentCodeAddress, Integer.MIN_VALUE));
|
||||||
|
}
|
||||||
|
methodItems.add(new CommentMethodItem(stg, getPostInstructionRegisterString(instruction),
|
||||||
|
currentCodeAddress, Integer.MAX_VALUE-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
addTries(methodItems);
|
addTries(methodItems);
|
||||||
@ -264,6 +285,41 @@ public class MethodDefinition {
|
|||||||
return methodItems;
|
return methodItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPreInstructionRegisterString(AnalyzedInstruction instruction) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i=0; i<instruction.getRegisterCount(); i++) {
|
||||||
|
RegisterType registerType = instruction.getPreInstructionRegisterType(i);
|
||||||
|
sb.append("v");
|
||||||
|
sb.append(i);
|
||||||
|
sb.append("=");
|
||||||
|
if (registerType == null) {
|
||||||
|
sb.append("null");
|
||||||
|
} else {
|
||||||
|
sb.append(registerType.toString());
|
||||||
|
}
|
||||||
|
sb.append(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPostInstructionRegisterString(AnalyzedInstruction instruction) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i=0; i<instruction.getRegisterCount(); i++) {
|
||||||
|
RegisterType registerType = instruction.getPostInstructionRegisterType(i);
|
||||||
|
sb.append("v");
|
||||||
|
sb.append(i);
|
||||||
|
sb.append("=");
|
||||||
|
sb.append(registerType.toString());
|
||||||
|
sb.append(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void addTries(List<MethodItem> methodItems) {
|
private void addTries(List<MethodItem> methodItems) {
|
||||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
|
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -33,6 +33,7 @@ import org.antlr.stringtemplate.StringTemplateGroup;
|
|||||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||||
import org.jf.baksmali.Deodex.*;
|
import org.jf.baksmali.Deodex.*;
|
||||||
import org.jf.baksmali.Renderers.*;
|
import org.jf.baksmali.Renderers.*;
|
||||||
|
import org.jf.dexlib.Code.Analysis.ClassPath;
|
||||||
import org.jf.dexlib.DexFile;
|
import org.jf.dexlib.DexFile;
|
||||||
import org.jf.dexlib.ClassDefItem;
|
import org.jf.dexlib.ClassDefItem;
|
||||||
import org.jf.dexlib.StringIdItem;
|
import org.jf.dexlib.StringIdItem;
|
||||||
@ -44,16 +45,25 @@ public class baksmali {
|
|||||||
public static boolean useLocalsDirective = false;
|
public static boolean useLocalsDirective = false;
|
||||||
public static boolean useSequentialLabels = false;
|
public static boolean useSequentialLabels = false;
|
||||||
public static boolean outputDebugInfo = true;
|
public static boolean outputDebugInfo = true;
|
||||||
|
public static boolean verboseRegisterInfo = false;
|
||||||
|
public static String bootClassPath;
|
||||||
public static DeodexUtil deodexUtil = null;
|
public static DeodexUtil deodexUtil = null;
|
||||||
|
|
||||||
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
|
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
|
||||||
boolean noParameterRegisters, boolean useLocalsDirective,
|
String bootClassPath, boolean noParameterRegisters,
|
||||||
boolean useSequentialLabels, boolean outputDebugInfo)
|
boolean useLocalsDirective, boolean useSequentialLabels,
|
||||||
|
boolean outputDebugInfo, boolean verboseRegisterInfo)
|
||||||
{
|
{
|
||||||
baksmali.noParameterRegisters = noParameterRegisters;
|
baksmali.noParameterRegisters = noParameterRegisters;
|
||||||
baksmali.useLocalsDirective = useLocalsDirective;
|
baksmali.useLocalsDirective = useLocalsDirective;
|
||||||
baksmali.useSequentialLabels = useSequentialLabels;
|
baksmali.useSequentialLabels = useSequentialLabels;
|
||||||
baksmali.outputDebugInfo = outputDebugInfo;
|
baksmali.outputDebugInfo = outputDebugInfo;
|
||||||
|
baksmali.verboseRegisterInfo = verboseRegisterInfo;
|
||||||
|
baksmali.bootClassPath = bootClassPath;
|
||||||
|
|
||||||
|
if (verboseRegisterInfo) {
|
||||||
|
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
|
||||||
|
}
|
||||||
|
|
||||||
if (deodexerant != null) {
|
if (deodexerant != null) {
|
||||||
baksmali.deodexUtil = new DeodexUtil(deodexerant);
|
baksmali.deodexUtil = new DeodexUtil(deodexerant);
|
||||||
|
@ -75,13 +75,14 @@ public class main {
|
|||||||
boolean useLocalsDirective = false;
|
boolean useLocalsDirective = false;
|
||||||
boolean useSequentialLabels = false;
|
boolean useSequentialLabels = false;
|
||||||
boolean outputDebugInfo = true;
|
boolean outputDebugInfo = true;
|
||||||
|
boolean verboseRegisterInfo = false;
|
||||||
|
|
||||||
String outputDirectory = "out";
|
String outputDirectory = "out";
|
||||||
String dumpFileName = null;
|
String dumpFileName = null;
|
||||||
String outputDexFileName = null;
|
String outputDexFileName = null;
|
||||||
String inputDexFileName = null;
|
String inputDexFileName = null;
|
||||||
String deodexerantHost = null;
|
String deodexerantHost = null;
|
||||||
|
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||||
int deodexerantPort = 0;
|
int deodexerantPort = 0;
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
String[] remainingArgs = commandLine.getArgs();
|
||||||
@ -145,6 +146,14 @@ public class main {
|
|||||||
outputDebugInfo = false;
|
outputDebugInfo = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("r")) {
|
||||||
|
verboseRegisterInfo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("c")) {
|
||||||
|
bootClassPath = commandLine.getOptionValue("c");
|
||||||
|
}
|
||||||
|
|
||||||
if (commandLine.hasOption("x")) {
|
if (commandLine.hasOption("x")) {
|
||||||
String deodexerantAddress = commandLine.getOptionValue("x");
|
String deodexerantAddress = commandLine.getOptionValue("x");
|
||||||
String[] parts = deodexerantAddress.split(":");
|
String[] parts = deodexerantAddress.split(":");
|
||||||
@ -200,8 +209,8 @@ public class main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (disassemble) {
|
if (disassemble) {
|
||||||
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, noParameterRegisters,
|
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters,
|
||||||
useLocalsDirective, useSequentialLabels, outputDebugInfo);
|
useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((doDump || write) && !dexFile.isOdex()) {
|
if ((doDump || write) && !dexFile.isOdex()) {
|
||||||
@ -310,6 +319,16 @@ public class main {
|
|||||||
.withDescription("don't write out debug info (.local, .param, .line, etc.)")
|
.withDescription("don't write out debug info (.local, .param, .line, etc.)")
|
||||||
.create("b");
|
.create("b");
|
||||||
|
|
||||||
|
Option verboseRegisterInfoOption = OptionBuilder.withLongOpt("verbose-registers")
|
||||||
|
.withDescription("print verbose register type information for each instruction")
|
||||||
|
.create("r");
|
||||||
|
|
||||||
|
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
||||||
|
.withDescription("the bootclasspath jars to use, for analysis")
|
||||||
|
.hasArg()
|
||||||
|
.withArgName("BOOTCLASSPATH")
|
||||||
|
.create("c");
|
||||||
|
|
||||||
options.addOption(versionOption);
|
options.addOption(versionOption);
|
||||||
options.addOption(helpOption);
|
options.addOption(helpOption);
|
||||||
options.addOption(dumpOption);
|
options.addOption(dumpOption);
|
||||||
@ -323,5 +342,7 @@ public class main {
|
|||||||
options.addOption(useLocalsOption);
|
options.addOption(useLocalsOption);
|
||||||
options.addOption(sequentialLabelsOption);
|
options.addOption(sequentialLabelsOption);
|
||||||
options.addOption(noDebugInfoOption);
|
options.addOption(noDebugInfoOption);
|
||||||
|
options.addOption(verboseRegisterInfoOption);
|
||||||
|
options.addOption(classPathOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -434,4 +434,9 @@ AnnotationEncodedValue(AnnotationType, Elements) ::=
|
|||||||
AnnotationElement(Name, Value) ::=
|
AnnotationElement(Name, Value) ::=
|
||||||
<<
|
<<
|
||||||
<Name> = <Value>
|
<Name> = <Value>
|
||||||
|
>>
|
||||||
|
|
||||||
|
Comment(Comment) ::=
|
||||||
|
<<
|
||||||
|
#<Comment>
|
||||||
>>
|
>>
|
@ -12,7 +12,7 @@ public class AnalyzedInstruction {
|
|||||||
/**
|
/**
|
||||||
* The actual instruction
|
* The actual instruction
|
||||||
*/
|
*/
|
||||||
protected final Instruction instruction;
|
public final Instruction instruction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the instruction, where the first instruction in the method is at index 0, and so on
|
* The index of the instruction, where the first instruction in the method is at index 0, and so on
|
||||||
@ -49,12 +49,20 @@ public class AnalyzedInstruction {
|
|||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
this.instructionIndex = instructionIndex;
|
this.instructionIndex = instructionIndex;
|
||||||
this.postRegisterMap = new RegisterType[registerCount];
|
this.postRegisterMap = new RegisterType[registerCount];
|
||||||
|
RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
|
||||||
|
for (int i=0; i<registerCount; i++) {
|
||||||
|
postRegisterMap[i] = unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInstructionIndex() {
|
public int getInstructionIndex() {
|
||||||
return instructionIndex;
|
return instructionIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPredecessorCount() {
|
||||||
|
return predecessors.size();
|
||||||
|
}
|
||||||
|
|
||||||
protected void addPredecessor(AnalyzedInstruction predecessor) {
|
protected void addPredecessor(AnalyzedInstruction predecessor) {
|
||||||
predecessors.add(predecessor);
|
predecessors.add(predecessor);
|
||||||
}
|
}
|
||||||
@ -104,7 +112,8 @@ public class AnalyzedInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isInvokeInit() {
|
protected boolean isInvokeInit() {
|
||||||
if (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE) {
|
if (instruction == null ||
|
||||||
|
(instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +124,7 @@ public class AnalyzedInstruction {
|
|||||||
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
|
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
|
||||||
MethodIdItem method = (MethodIdItem)item;
|
MethodIdItem method = (MethodIdItem)item;
|
||||||
|
|
||||||
if (!method.getMethodName().equals("<init>")) {
|
if (!method.getMethodName().getStringValue().equals("<init>")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +184,14 @@ public class AnalyzedInstruction {
|
|||||||
return ((SingleRegisterInstruction)instruction).getRegisterA();
|
return ((SingleRegisterInstruction)instruction).getRegisterA();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRegisterCount() {
|
||||||
|
return postRegisterMap.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegisterType getPostInstructionRegisterType(int registerNumber) {
|
||||||
|
return postRegisterMap[registerNumber];
|
||||||
|
}
|
||||||
|
|
||||||
public RegisterType getPreInstructionRegisterType(int registerNumber) {
|
public RegisterType getPreInstructionRegisterType(int registerNumber) {
|
||||||
//if the specific register is not a destination register, then the stored post-instruction register type will
|
//if the specific register is not a destination register, then the stored post-instruction register type will
|
||||||
//be the same as the pre-instruction regsiter type, so we can use that.
|
//be the same as the pre-instruction regsiter type, so we can use that.
|
||||||
|
@ -15,35 +15,32 @@ public class ClassPath {
|
|||||||
private static ClassPath theClassPath = null;
|
private static ClassPath theClassPath = null;
|
||||||
|
|
||||||
private final HashMap<String, ClassDef> classDefs;
|
private final HashMap<String, ClassDef> classDefs;
|
||||||
protected final ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
|
protected ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
|
||||||
|
|
||||||
public static void InitializeClassPath(String[] bootClassPath, DexFile dexFile) {
|
public static void InitializeClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||||
if (theClassPath != null) {
|
if (theClassPath != null) {
|
||||||
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
||||||
}
|
}
|
||||||
|
|
||||||
theClassPath = new ClassPath(bootClassPath, dexFile);
|
theClassPath = new ClassPath();
|
||||||
|
theClassPath.initClassPath(bootClassPath, dexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassPath(String[] bootClassPath, DexFile dexFile) {
|
private ClassPath() {
|
||||||
|
classDefs = new HashMap<String, ClassDef>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||||
if (bootClassPath == null || bootClassPath.length == 0) {
|
if (bootClassPath == null || bootClassPath.length == 0) {
|
||||||
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
|
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
|
||||||
}
|
}
|
||||||
|
|
||||||
classDefs = new HashMap<String, ClassDef>();
|
|
||||||
|
|
||||||
for (String bootClassPathEntry: bootClassPath) {
|
for (String bootClassPathEntry: bootClassPath) {
|
||||||
loadBootClassPath(bootClassPathEntry);
|
loadBootClassPath(bootClassPathEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDexFile(dexFile);
|
loadDexFile(dexFile);
|
||||||
|
|
||||||
try {
|
|
||||||
javaLangObjectClassDef = getClassDef("Ljava/lang/Object;");
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
throw ExceptionWithContext.withContext(ex, "Ljava/lang/Object; must be present in the classpath");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
|
for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
|
||||||
ClassDef classDef = new PrimitiveClassDef(primitiveType);
|
ClassDef classDef = new PrimitiveClassDef(primitiveType);
|
||||||
classDefs.put(primitiveType, classDef);
|
classDefs.put(primitiveType, classDef);
|
||||||
@ -77,8 +74,12 @@ public class ClassPath {
|
|||||||
//TODO: need to check if the class already exists. (and if so, what to do about it?)
|
//TODO: need to check if the class already exists. (and if so, what to do about it?)
|
||||||
ClassDef classDef = new ClassDef(classDefItem);
|
ClassDef classDef = new ClassDef(classDefItem);
|
||||||
classDefs.put(classDef.getClassType(), classDef);
|
classDefs.put(classDef.getClassType(), classDef);
|
||||||
classDef.dumpVtable();
|
|
||||||
classDef.dumpFields();
|
if (classDefItem.getClassType().getTypeDescriptor().equals("Ljava/lang/Object;")) {
|
||||||
|
theClassPath.javaLangObjectClassDef = classDef;
|
||||||
|
}
|
||||||
|
/*classDef.dumpVtable();
|
||||||
|
classDef.dumpFields();*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +275,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ClassDef {
|
public static class ClassDef implements Comparable<ClassDef> {
|
||||||
private final String classType;
|
private final String classType;
|
||||||
private final ClassDef superclass;
|
private final ClassDef superclass;
|
||||||
/**
|
/**
|
||||||
@ -329,7 +330,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ClassDef(ClassDefItem classDefItem) {
|
protected ClassDef(ClassDefItem classDefItem) {
|
||||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||||
|
|
||||||
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
||||||
@ -406,23 +407,23 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: GROT
|
//TODO: GROT
|
||||||
public void dumpVtable() {
|
/*public void dumpVtable() {
|
||||||
System.out.println(classType + " methods:");
|
System.out.println(classType + " methods:");
|
||||||
int i=0;
|
int i=0;
|
||||||
for (String method: vtable) {
|
for (String method: vtable) {
|
||||||
System.out.println(i + ":\t" + method);
|
System.out.println(i + ":\t" + method);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//TODO: GROT
|
//TODO: GROT
|
||||||
public void dumpFields() {
|
/*public void dumpFields() {
|
||||||
System.out.println(classType + " fields:");
|
System.out.println(classType + " fields:");
|
||||||
for (int i=0; i<instanceFields.size(); i++) {
|
for (int i=0; i<instanceFields.size(); i++) {
|
||||||
int fieldOffset = instanceFields.keyAt(i);
|
int fieldOffset = instanceFields.keyAt(i);
|
||||||
System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i));
|
System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i));
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
|
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
|
||||||
byte tempType = fieldTypes[position1];
|
byte tempType = fieldTypes[position1];
|
||||||
@ -725,5 +726,9 @@ public class ClassPath {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return classType.hashCode();
|
return classType.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int compareTo(ClassDef classDef) {
|
||||||
|
return classType.compareTo(classDef.classType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ public class MethodAnalyzer {
|
|||||||
throw new IllegalArgumentException("The method has no code");
|
throw new IllegalArgumentException("The method has no code");
|
||||||
}
|
}
|
||||||
this.encodedMethod = encodedMethod;
|
this.encodedMethod = encodedMethod;
|
||||||
buildInstructionList();
|
|
||||||
|
|
||||||
//override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
|
//override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
|
||||||
//have to handle the case this special case of instruction being null, in the main class
|
//have to handle the case this special case of instruction being null, in the main class
|
||||||
@ -57,6 +56,8 @@ public class MethodAnalyzer {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
buildInstructionList();
|
||||||
|
|
||||||
instructionsToVerify = new BitSet(instructions.size());
|
instructionsToVerify = new BitSet(instructions.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ public class MethodAnalyzer {
|
|||||||
//if this is a constructor, then set the "this" register to an uninitialized reference of the current class
|
//if this is a constructor, then set the "this" register to an uninitialized reference of the current class
|
||||||
if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
|
if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
|
||||||
//TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor
|
//TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor
|
||||||
if (!encodedMethod.method.getMethodName().equals("<init>")) {
|
if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||||
throw new ValidationException("The constructor flag can only be used with an <init> method.");
|
throw new ValidationException("The constructor flag can only be used with an <init> method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ public class MethodAnalyzer {
|
|||||||
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
|
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
|
||||||
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
||||||
} else {
|
} else {
|
||||||
if (encodedMethod.method.getMethodName().equals("<init>")) {
|
if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||||
throw new ValidationException("An <init> method must have the \"constructor\" access flag");
|
throw new ValidationException("An <init> method must have the \"constructor\" access flag");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +120,8 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
while (!instructionsToVerify.isEmpty()) {
|
while (!instructionsToVerify.isEmpty()) {
|
||||||
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
|
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
|
||||||
analyzeInstruction(instructions.get(i));
|
|
||||||
instructionsToVerify.clear(i);
|
instructionsToVerify.clear(i);
|
||||||
|
analyzeInstruction(instructions.valueAt(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +202,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType);
|
boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType);
|
||||||
|
|
||||||
if (!changed || analyzedInstruction.setsRegister(registerNumber)) {
|
if (!changed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ public class MethodAnalyzer {
|
|||||||
while (!changedInstructions.isEmpty()) {
|
while (!changedInstructions.isEmpty()) {
|
||||||
for (int instructionIndex=changedInstructions.nextSetBit(0);
|
for (int instructionIndex=changedInstructions.nextSetBit(0);
|
||||||
instructionIndex>=0;
|
instructionIndex>=0;
|
||||||
instructionIndex=changedInstructions.nextSetBit(instructionIndex)) {
|
instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
|
||||||
|
|
||||||
changedInstructions.clear(instructionIndex);
|
changedInstructions.clear(instructionIndex);
|
||||||
|
|
||||||
@ -242,9 +243,10 @@ public class MethodAnalyzer {
|
|||||||
if (!successor.setsRegister(registerNumber)) {
|
if (!successor.setsRegister(registerNumber)) {
|
||||||
RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber);
|
RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber);
|
||||||
|
|
||||||
if (registerType.category == RegisterType.Category.UninitRef && instruction.isInvokeInit()) {
|
//TODO: GROT?
|
||||||
|
/*if (registerType.category == RegisterType.Category.UninitRef && instruction.isInvokeInit()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
||||||
changedInstructions.set(successor.instructionIndex);
|
changedInstructions.set(successor.instructionIndex);
|
||||||
@ -261,8 +263,6 @@ public class MethodAnalyzer {
|
|||||||
assert encodedMethod.codeItem != null;
|
assert encodedMethod.codeItem != null;
|
||||||
int registerCount = encodedMethod.codeItem.getRegisterCount();
|
int registerCount = encodedMethod.codeItem.getRegisterCount();
|
||||||
|
|
||||||
startOfMethod = new AnalyzedInstruction(null, -1, registerCount);
|
|
||||||
|
|
||||||
Instruction[] insns = encodedMethod.codeItem.getInstructions();
|
Instruction[] insns = encodedMethod.codeItem.getInstructions();
|
||||||
|
|
||||||
instructions = new SparseArray<AnalyzedInstruction>(insns.length);
|
instructions = new SparseArray<AnalyzedInstruction>(insns.length);
|
||||||
@ -284,34 +284,36 @@ public class MethodAnalyzer {
|
|||||||
AnalyzedInstruction[] currentExceptionHandlers = null;
|
AnalyzedInstruction[] currentExceptionHandlers = null;
|
||||||
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
|
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
|
||||||
|
|
||||||
for (int i=0; i<instructions.size(); i++) {
|
if (tries != null) {
|
||||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||||
|
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||||
|
|
||||||
//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) {
|
||||||
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
||||||
currentTry = null;
|
currentTry = null;
|
||||||
triesIndex++;
|
triesIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//check if the next try is applicable yet
|
//check if the next try is applicable yet
|
||||||
if (currentTry == null && triesIndex < tries.length) {
|
if (currentTry == null && triesIndex < tries.length) {
|
||||||
CodeItem.TryItem tryItem = tries[triesIndex];
|
CodeItem.TryItem tryItem = tries[triesIndex];
|
||||||
if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
|
if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
|
||||||
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
|
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
|
||||||
|
|
||||||
currentTry = tryItem;
|
currentTry = tryItem;
|
||||||
|
|
||||||
currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
|
currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
|
//if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
|
||||||
//for the current instruction
|
//for the current instruction
|
||||||
if (currentTry != null && instructionOpcode.canThrow()) {
|
if (currentTry != null && instructionOpcode.canThrow()) {
|
||||||
exceptionHandlers[i] = currentExceptionHandlers;
|
exceptionHandlers[i] = currentExceptionHandlers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,8 +335,8 @@ public class MethodAnalyzer {
|
|||||||
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
|
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instruction instanceof OffsetInstruction) {
|
if (instruction.instruction instanceof OffsetInstruction) {
|
||||||
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
|
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
|
||||||
|
|
||||||
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
|
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
|
||||||
MultiOffsetInstruction switchDataInstruction =
|
MultiOffsetInstruction switchDataInstruction =
|
||||||
@ -431,7 +433,7 @@ public class MethodAnalyzer {
|
|||||||
case MOVE_OBJECT:
|
case MOVE_OBJECT:
|
||||||
case MOVE_OBJECT_FROM16:
|
case MOVE_OBJECT_FROM16:
|
||||||
case MOVE_OBJECT_16:
|
case MOVE_OBJECT_16:
|
||||||
handleMove(analyzedInstruction, ReferenceCategories);
|
handleMove(analyzedInstruction, ReferenceOrUninitCategories);
|
||||||
return;
|
return;
|
||||||
case MOVE_RESULT:
|
case MOVE_RESULT:
|
||||||
handleMoveResult(analyzedInstruction, Primitive32BitCategories);
|
handleMoveResult(analyzedInstruction, Primitive32BitCategories);
|
||||||
@ -904,9 +906,12 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
|
private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
|
||||||
RegisterType.Category.Null,
|
RegisterType.Category.Null,
|
||||||
|
RegisterType.Category.One,
|
||||||
RegisterType.Category.Boolean,
|
RegisterType.Category.Boolean,
|
||||||
RegisterType.Category.Byte,
|
RegisterType.Category.Byte,
|
||||||
|
RegisterType.Category.PosByte,
|
||||||
RegisterType.Category.Short,
|
RegisterType.Category.Short,
|
||||||
|
RegisterType.Category.PosShort,
|
||||||
RegisterType.Category.Char,
|
RegisterType.Category.Char,
|
||||||
RegisterType.Category.Integer,
|
RegisterType.Category.Integer,
|
||||||
RegisterType.Category.Float);
|
RegisterType.Category.Float);
|
||||||
@ -923,11 +928,19 @@ public class MethodAnalyzer {
|
|||||||
RegisterType.Category.Null,
|
RegisterType.Category.Null,
|
||||||
RegisterType.Category.Reference);
|
RegisterType.Category.Reference);
|
||||||
|
|
||||||
|
private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
|
||||||
|
RegisterType.Category.Null,
|
||||||
|
RegisterType.Category.UninitRef,
|
||||||
|
RegisterType.Category.Reference);
|
||||||
|
|
||||||
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
||||||
RegisterType.Category.Null,
|
RegisterType.Category.Null,
|
||||||
|
RegisterType.Category.One,
|
||||||
RegisterType.Category.Boolean,
|
RegisterType.Category.Boolean,
|
||||||
RegisterType.Category.Byte,
|
RegisterType.Category.Byte,
|
||||||
|
RegisterType.Category.PosByte,
|
||||||
RegisterType.Category.Short,
|
RegisterType.Category.Short,
|
||||||
|
RegisterType.Category.PosShort,
|
||||||
RegisterType.Category.Char,
|
RegisterType.Category.Char,
|
||||||
RegisterType.Category.Integer,
|
RegisterType.Category.Integer,
|
||||||
RegisterType.Category.Float,
|
RegisterType.Category.Float,
|
||||||
@ -1037,8 +1050,9 @@ public class MethodAnalyzer {
|
|||||||
if (thisRegisterType.category == RegisterType.Category.UninitRef) {
|
if (thisRegisterType.category == RegisterType.Category.UninitRef) {
|
||||||
throw new ValidationException("Returning from constructor without calling the superclass' <init>");
|
throw new ValidationException("Returning from constructor without calling the superclass' <init>");
|
||||||
}
|
}
|
||||||
assert thisRegisterType.category == RegisterType.Category.Reference;
|
//TODO: GROT
|
||||||
assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
//assert thisRegisterType.category == RegisterType.Category.Reference;
|
||||||
|
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
||||||
@ -2285,8 +2299,8 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isInit) {
|
if (isInit) {
|
||||||
if (objectRegisterType.type == methodClassDef.getSuperclass()) {
|
if (objectRegisterType.type.getSuperclass() == methodClassDef) {
|
||||||
if (!encodedMethod.method.getMethodName().equals("<init>")) {
|
if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||||
throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
|
throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
|
||||||
"match the method type exactly", methodIdItem.getMethodString(),
|
"match the method type exactly", methodIdItem.getMethodString(),
|
||||||
objectRegisterType.type.getClassType()));
|
objectRegisterType.type.getClassType()));
|
||||||
@ -2302,44 +2316,46 @@ public class MethodAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
if (typeListItem != null) {
|
||||||
int parameterTypeIndex = 0;
|
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
||||||
while (!registers.pastEnd()) {
|
int parameterTypeIndex = 0;
|
||||||
assert parameterTypeIndex < parameterTypes.size();
|
while (!registers.pastEnd()) {
|
||||||
RegisterType parameterType =
|
assert parameterTypeIndex < parameterTypes.size();
|
||||||
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
RegisterType parameterType =
|
||||||
|
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
||||||
|
|
||||||
int register = registers.getRegister();
|
int register = registers.getRegister();
|
||||||
|
|
||||||
RegisterType parameterRegisterType;
|
RegisterType parameterRegisterType;
|
||||||
if (WideLowCategories.contains(parameterType.category)) {
|
if (WideLowCategories.contains(parameterType.category)) {
|
||||||
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
||||||
|
|
||||||
if (!registers.moveNext()) {
|
if (!registers.moveNext()) {
|
||||||
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
||||||
parameterTypeIndex+1));
|
parameterTypeIndex+1));
|
||||||
|
}
|
||||||
|
int nextRegister = registers.getRegister();
|
||||||
|
|
||||||
|
if (nextRegister != register + 1) {
|
||||||
|
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
|
||||||
|
"must be consecutive.", register, nextRegister));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
|
||||||
}
|
}
|
||||||
int nextRegister = registers.getRegister();
|
|
||||||
|
|
||||||
if (nextRegister != register + 1) {
|
assert parameterRegisterType != null;
|
||||||
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
|
|
||||||
"must be consecutive.", register, nextRegister));
|
if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
|
||||||
|
throw new ValidationException(
|
||||||
|
String.format("Invalid register type %s for parameter %d %s.",
|
||||||
|
parameterRegisterType.toString(), parameterTypeIndex+1,
|
||||||
|
parameterType.toString()));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
|
parameterTypeIndex++;
|
||||||
|
registers.moveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert parameterRegisterType != null;
|
|
||||||
|
|
||||||
if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
|
|
||||||
throw new ValidationException(
|
|
||||||
String.format("Invalid register type %s for parameter %d %s.",
|
|
||||||
parameterRegisterType.toString(), parameterTypeIndex+1,
|
|
||||||
parameterType.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
parameterTypeIndex++;
|
|
||||||
registers.moveNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,18 +222,18 @@ public class RegisterType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
|
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
|
||||||
if (mergedCategory == Category.Conflicted) {
|
|
||||||
throw new ValidationException("Incompatible register types." +
|
|
||||||
" Category1: " + this.category.name() +
|
|
||||||
(this.type==null?"":" Type1: " + this.type.getClassType()) +
|
|
||||||
" Category2: " + type.category.name() +
|
|
||||||
(type.type==null?"":" Type2: " + type.type.getClassType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassDef mergedType = null;
|
ClassDef mergedType = null;
|
||||||
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 (this.category == Category.Unknown) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
assert type.category == Category.Unknown;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
return RegisterType.getRegisterType(mergedCategory, mergedType);
|
return RegisterType.getRegisterType(mergedCategory, mergedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,9 +281,9 @@ public enum Opcode
|
|||||||
//if the instruction sets the "hidden" result register
|
//if the instruction sets the "hidden" result register
|
||||||
public static final int SETS_RESULT = 0x8;
|
public static final int SETS_RESULT = 0x8;
|
||||||
//if the instruction sets the value of it's first register
|
//if the instruction sets the value of it's first register
|
||||||
public static final int SETS_REGISTER = 0x16;
|
public static final int SETS_REGISTER = 0x10;
|
||||||
//if the instruction sets the value of it's first register to a wide type
|
//if the instruction sets the value of it's first register to a wide type
|
||||||
public static final int SETS_WIDE_REGISTER = 0x32;
|
public static final int SETS_WIDE_REGISTER = 0x20;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
opcodesByValue = new Opcode[256];
|
opcodesByValue = new Opcode[256];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user