mirror of
https://github.com/revanced/smali.git
synced 2025-06-12 12:17:37 +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:
@ -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.
|
||||
|
@ -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;
|
||||
/**
|
||||
@ -329,7 +330,7 @@ public class ClassPath {
|
||||
}
|
||||
}
|
||||
|
||||
protected ClassDef(ClassDefItem classDefItem) {
|
||||
protected ClassDef(ClassDefItem classDefItem) {
|
||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||
|
||||
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,34 +284,36 @@ public class MethodAnalyzer {
|
||||
AnalyzedInstruction[] currentExceptionHandlers = null;
|
||||
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
|
||||
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||
if (tries != null) {
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||
|
||||
//check if we have gone past the end of the current try
|
||||
if (currentTry != null) {
|
||||
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
||||
currentTry = null;
|
||||
triesIndex++;
|
||||
//check if we have gone past the end of the current try
|
||||
if (currentTry != null) {
|
||||
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
||||
currentTry = null;
|
||||
triesIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if the next try is applicable yet
|
||||
if (currentTry == null && triesIndex < tries.length) {
|
||||
CodeItem.TryItem tryItem = tries[triesIndex];
|
||||
if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
|
||||
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
|
||||
//check if the next try is applicable yet
|
||||
if (currentTry == null && triesIndex < tries.length) {
|
||||
CodeItem.TryItem tryItem = tries[triesIndex];
|
||||
if (tryItem.getStartCodeAddress() <= 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
|
||||
//for the current instruction
|
||||
if (currentTry != null && instructionOpcode.canThrow()) {
|
||||
exceptionHandlers[i] = currentExceptionHandlers;
|
||||
//if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
|
||||
//for the current instruction
|
||||
if (currentTry != null && instructionOpcode.canThrow()) {
|
||||
exceptionHandlers[i] = currentExceptionHandlers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,44 +2316,46 @@ public class MethodAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
||||
int parameterTypeIndex = 0;
|
||||
while (!registers.pastEnd()) {
|
||||
assert parameterTypeIndex < parameterTypes.size();
|
||||
RegisterType parameterType =
|
||||
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
||||
if (typeListItem != null) {
|
||||
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
||||
int parameterTypeIndex = 0;
|
||||
while (!registers.pastEnd()) {
|
||||
assert parameterTypeIndex < parameterTypes.size();
|
||||
RegisterType parameterType =
|
||||
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
||||
|
||||
int register = registers.getRegister();
|
||||
int register = registers.getRegister();
|
||||
|
||||
RegisterType parameterRegisterType;
|
||||
if (WideLowCategories.contains(parameterType.category)) {
|
||||
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
||||
RegisterType parameterRegisterType;
|
||||
if (WideLowCategories.contains(parameterType.category)) {
|
||||
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
||||
|
||||
if (!registers.moveNext()) {
|
||||
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
||||
parameterTypeIndex+1));
|
||||
if (!registers.moveNext()) {
|
||||
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
||||
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) {
|
||||
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
|
||||
"must be consecutive.", register, nextRegister));
|
||||
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()));
|
||||
}
|
||||
} 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()];
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
Reference in New Issue
Block a user