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:
JesusFreke@JesusFreke.com 2010-02-08 02:28:37 +00:00
parent caea37a7d2
commit 7e24a9f010
10 changed files with 269 additions and 114 deletions

View File

@ -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();
}
}

View File

@ -31,6 +31,9 @@ package org.jf.baksmali.Adaptors;
import org.jf.baksmali.Adaptors.Format.*;
import org.jf.baksmali.baksmali;
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.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
@ -45,6 +48,7 @@ import java.util.*;
public class MethodDefinition {
private final StringTemplateGroup stg;
private final ClassDataItem.EncodedMethod encodedMethod;
private final MethodAnalyzer methodAnalyzer;
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.
if (encodedMethod.codeItem != null) {
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
methodAnalyzer = new MethodAnalyzer(encodedMethod);
AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
packedSwitchMap = new SparseIntArray(1);
sparseSwitchMap = new SparseIntArray(1);
@ -67,23 +72,24 @@ public class MethodDefinition {
int currentCodeAddress = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
if (instruction.opcode == Opcode.PACKED_SWITCH) {
AnalyzedInstruction instruction = instructions[i];
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
packedSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
currentCodeAddress);
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
sparseSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
currentCodeAddress);
}
instructionMap.append(currentCodeAddress, i);
currentCodeAddress += instruction.getSize(currentCodeAddress);
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
}
} else {
packedSwitchMap = null;
sparseSwitchMap = null;
instructionMap = null;
methodAnalyzer = null;
}
}
@ -227,20 +233,35 @@ public class MethodDefinition {
return methodItems;
}
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
AnalyzedInstruction[] instructions;
if (baksmali.verboseRegisterInfo) {
instructions = methodAnalyzer.analyze();
} else {
instructions = methodAnalyzer.makeInstructionArray();
}
int currentCodeAddress = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
AnalyzedInstruction instruction = instructions[i];
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, stg, instruction));
encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
if (i != instructions.length - 1) {
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);
@ -264,6 +285,41 @@ public class MethodDefinition {
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) {
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
return;

View File

@ -33,6 +33,7 @@ import org.antlr.stringtemplate.StringTemplateGroup;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Deodex.*;
import org.jf.baksmali.Renderers.*;
import org.jf.dexlib.Code.Analysis.ClassPath;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.StringIdItem;
@ -44,16 +45,25 @@ public class baksmali {
public static boolean useLocalsDirective = false;
public static boolean useSequentialLabels = false;
public static boolean outputDebugInfo = true;
public static boolean verboseRegisterInfo = false;
public static String bootClassPath;
public static DeodexUtil deodexUtil = null;
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
boolean noParameterRegisters, boolean useLocalsDirective,
boolean useSequentialLabels, boolean outputDebugInfo)
String bootClassPath, boolean noParameterRegisters,
boolean useLocalsDirective, boolean useSequentialLabels,
boolean outputDebugInfo, boolean verboseRegisterInfo)
{
baksmali.noParameterRegisters = noParameterRegisters;
baksmali.useLocalsDirective = useLocalsDirective;
baksmali.useSequentialLabels = useSequentialLabels;
baksmali.outputDebugInfo = outputDebugInfo;
baksmali.verboseRegisterInfo = verboseRegisterInfo;
baksmali.bootClassPath = bootClassPath;
if (verboseRegisterInfo) {
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
}
if (deodexerant != null) {
baksmali.deodexUtil = new DeodexUtil(deodexerant);

View File

@ -75,13 +75,14 @@ public class main {
boolean useLocalsDirective = false;
boolean useSequentialLabels = false;
boolean outputDebugInfo = true;
boolean verboseRegisterInfo = false;
String outputDirectory = "out";
String dumpFileName = null;
String outputDexFileName = null;
String inputDexFileName = null;
String deodexerantHost = null;
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
int deodexerantPort = 0;
String[] remainingArgs = commandLine.getArgs();
@ -145,6 +146,14 @@ public class main {
outputDebugInfo = false;
}
if (commandLine.hasOption("r")) {
verboseRegisterInfo = true;
}
if (commandLine.hasOption("c")) {
bootClassPath = commandLine.getOptionValue("c");
}
if (commandLine.hasOption("x")) {
String deodexerantAddress = commandLine.getOptionValue("x");
String[] parts = deodexerantAddress.split(":");
@ -200,8 +209,8 @@ public class main {
}
if (disassemble) {
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, noParameterRegisters,
useLocalsDirective, useSequentialLabels, outputDebugInfo);
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters,
useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo);
}
if ((doDump || write) && !dexFile.isOdex()) {
@ -310,6 +319,16 @@ public class main {
.withDescription("don't write out debug info (.local, .param, .line, etc.)")
.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(helpOption);
options.addOption(dumpOption);
@ -323,5 +342,7 @@ public class main {
options.addOption(useLocalsOption);
options.addOption(sequentialLabelsOption);
options.addOption(noDebugInfoOption);
options.addOption(verboseRegisterInfoOption);
options.addOption(classPathOption);
}
}

View File

@ -435,3 +435,8 @@ AnnotationElement(Name, Value) ::=
<<
<Name> = <Value>
>>
Comment(Comment) ::=
<<
#<Comment>
>>

View File

@ -12,7 +12,7 @@ public class AnalyzedInstruction {
/**
* 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
@ -49,12 +49,20 @@ public class AnalyzedInstruction {
this.instruction = instruction;
this.instructionIndex = instructionIndex;
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() {
return instructionIndex;
}
public int getPredecessorCount() {
return predecessors.size();
}
protected void addPredecessor(AnalyzedInstruction predecessor) {
predecessors.add(predecessor);
}
@ -104,7 +112,8 @@ public class AnalyzedInstruction {
}
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;
}
@ -115,7 +124,7 @@ public class AnalyzedInstruction {
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
MethodIdItem method = (MethodIdItem)item;
if (!method.getMethodName().equals("<init>")) {
if (!method.getMethodName().getStringValue().equals("<init>")) {
return false;
}
@ -175,6 +184,14 @@ public class AnalyzedInstruction {
return ((SingleRegisterInstruction)instruction).getRegisterA();
}
public int getRegisterCount() {
return postRegisterMap.length;
}
public RegisterType getPostInstructionRegisterType(int registerNumber) {
return postRegisterMap[registerNumber];
}
public RegisterType getPreInstructionRegisterType(int registerNumber) {
//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.

View File

@ -15,35 +15,32 @@ public class ClassPath {
private static ClassPath theClassPath = null;
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) {
if (theClassPath != null) {
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) {
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
}
classDefs = new HashMap<String, ClassDef>();
for (String bootClassPathEntry: bootClassPath) {
loadBootClassPath(bootClassPathEntry);
}
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"}) {
ClassDef classDef = new PrimitiveClassDef(primitiveType);
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?)
ClassDef classDef = new ClassDef(classDefItem);
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 ClassDef superclass;
/**
@ -406,23 +407,23 @@ public class ClassPath {
}
//TODO: GROT
public void dumpVtable() {
/*public void dumpVtable() {
System.out.println(classType + " methods:");
int i=0;
for (String method: vtable) {
System.out.println(i + ":\t" + method);
i++;
}
}
}*/
//TODO: GROT
public void dumpFields() {
/*public void dumpFields() {
System.out.println(classType + " fields:");
for (int i=0; i<instanceFields.size(); i++) {
int fieldOffset = instanceFields.keyAt(i);
System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i));
}
}
}*/
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
byte tempType = fieldTypes[position1];
@ -725,5 +726,9 @@ public class ClassPath {
public int hashCode() {
return classType.hashCode();
}
public int compareTo(ClassDef classDef) {
return classType.compareTo(classDef.classType);
}
}
}

View File

@ -31,7 +31,6 @@ public class MethodAnalyzer {
throw new IllegalArgumentException("The method has no code");
}
this.encodedMethod = encodedMethod;
buildInstructionList();
//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
@ -57,6 +56,8 @@ public class MethodAnalyzer {
};
};
buildInstructionList();
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 ((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
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.");
}
@ -90,7 +91,7 @@ public class MethodAnalyzer {
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
ClassPath.getClassDef(methodIdItem.getContainingClass())));
} 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");
}
@ -119,8 +120,8 @@ public class MethodAnalyzer {
while (!instructionsToVerify.isEmpty()) {
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
analyzeInstruction(instructions.get(i));
instructionsToVerify.clear(i);
analyzeInstruction(instructions.valueAt(i));
}
}
@ -201,7 +202,7 @@ public class MethodAnalyzer {
boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType);
if (!changed || analyzedInstruction.setsRegister(registerNumber)) {
if (!changed) {
return;
}
@ -216,7 +217,7 @@ public class MethodAnalyzer {
while (!changedInstructions.isEmpty()) {
for (int instructionIndex=changedInstructions.nextSetBit(0);
instructionIndex>=0;
instructionIndex=changedInstructions.nextSetBit(instructionIndex)) {
instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
changedInstructions.clear(instructionIndex);
@ -242,9 +243,10 @@ public class MethodAnalyzer {
if (!successor.setsRegister(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;
}
}*/
if (successor.setPostRegisterType(registerNumber, registerType)) {
changedInstructions.set(successor.instructionIndex);
@ -261,8 +263,6 @@ public class MethodAnalyzer {
assert encodedMethod.codeItem != null;
int registerCount = encodedMethod.codeItem.getRegisterCount();
startOfMethod = new AnalyzedInstruction(null, -1, registerCount);
Instruction[] insns = encodedMethod.codeItem.getInstructions();
instructions = new SparseArray<AnalyzedInstruction>(insns.length);
@ -284,6 +284,7 @@ public class MethodAnalyzer {
AnalyzedInstruction[] currentExceptionHandlers = null;
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
if (tries != null) {
for (int i=0; i<instructions.size(); i++) {
AnalyzedInstruction instruction = instructions.valueAt(i);
Opcode instructionOpcode = instruction.instruction.opcode;
@ -314,6 +315,7 @@ public class MethodAnalyzer {
exceptionHandlers[i] = currentExceptionHandlers;
}
}
}
//finally, populate the successors and predecessors for each instruction
assert instructions.size() > 0;
@ -333,8 +335,8 @@ public class MethodAnalyzer {
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
}
if (instruction instanceof OffsetInstruction) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
if (instruction.instruction instanceof OffsetInstruction) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
MultiOffsetInstruction switchDataInstruction =
@ -431,7 +433,7 @@ public class MethodAnalyzer {
case MOVE_OBJECT:
case MOVE_OBJECT_FROM16:
case MOVE_OBJECT_16:
handleMove(analyzedInstruction, ReferenceCategories);
handleMove(analyzedInstruction, ReferenceOrUninitCategories);
return;
case MOVE_RESULT:
handleMoveResult(analyzedInstruction, Primitive32BitCategories);
@ -904,9 +906,12 @@ public class MethodAnalyzer {
private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
RegisterType.Category.Null,
RegisterType.Category.One,
RegisterType.Category.Boolean,
RegisterType.Category.Byte,
RegisterType.Category.PosByte,
RegisterType.Category.Short,
RegisterType.Category.PosShort,
RegisterType.Category.Char,
RegisterType.Category.Integer,
RegisterType.Category.Float);
@ -923,11 +928,19 @@ public class MethodAnalyzer {
RegisterType.Category.Null,
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(
RegisterType.Category.Null,
RegisterType.Category.One,
RegisterType.Category.Boolean,
RegisterType.Category.Byte,
RegisterType.Category.PosByte,
RegisterType.Category.Short,
RegisterType.Category.PosShort,
RegisterType.Category.Char,
RegisterType.Category.Integer,
RegisterType.Category.Float,
@ -1037,8 +1050,9 @@ public class MethodAnalyzer {
if (thisRegisterType.category == RegisterType.Category.UninitRef) {
throw new ValidationException("Returning from constructor without calling the superclass' <init>");
}
assert thisRegisterType.category == RegisterType.Category.Reference;
assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
//TODO: GROT
//assert thisRegisterType.category == RegisterType.Category.Reference;
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
}
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
@ -2285,8 +2299,8 @@ public class MethodAnalyzer {
}
if (isInit) {
if (objectRegisterType.type == methodClassDef.getSuperclass()) {
if (!encodedMethod.method.getMethodName().equals("<init>")) {
if (objectRegisterType.type.getSuperclass() == methodClassDef) {
if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
"match the method type exactly", methodIdItem.getMethodString(),
objectRegisterType.type.getClassType()));
@ -2302,6 +2316,7 @@ public class MethodAnalyzer {
}
}
if (typeListItem != null) {
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
int parameterTypeIndex = 0;
while (!registers.pastEnd()) {
@ -2341,6 +2356,7 @@ public class MethodAnalyzer {
parameterTypeIndex++;
registers.moveNext();
}
}
//TODO: need to ensure the "this" register is initialized, in a constructor method

View File

@ -222,18 +222,18 @@ public class RegisterType {
}
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;
if (mergedCategory == Category.Reference) {
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);
}

View File

@ -281,9 +281,9 @@ public enum Opcode
//if the instruction sets the "hidden" result register
public static final int SETS_RESULT = 0x8;
//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
public static final int SETS_WIDE_REGISTER = 0x32;
public static final int SETS_WIDE_REGISTER = 0x20;
static {
opcodesByValue = new Opcode[256];