blob: ba782fe6a1147a97cf8bf8b09261c5a6fa3e0916 [file] [log] [blame]
/*
* Copyright 2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.analysis;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.Writer;
public class RegisterType {
public final byte category;
@Nullable public final TypeProto type;
private RegisterType(byte category, @Nullable TypeProto type) {
assert ((category == REFERENCE || category == UNINIT_REF || category == UNINIT_THIS) && type != null) ||
((category != REFERENCE && category != UNINIT_REF && category != UNINIT_THIS) && type == null);
this.category = category;
this.type = type;
}
@Override
public String toString() {
return "(" + CATEGORY_NAMES[category] + (type==null?"":("," + type)) + ")";
}
public void writeTo(Writer writer) throws IOException {
writer.write('(');
writer.write(CATEGORY_NAMES[category]);
if (type != null) {
writer.write(',');
writer.write(type.getType());
}
writer.write(')');
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RegisterType that = (RegisterType) o;
if (category != that.category) {
return false;
}
// These require strict reference equality. Every instance represents a unique
// reference that can't be merged with a different one, even if they have the same type.
if (category == UNINIT_REF || category == UNINIT_THIS) {
return false;
}
return (type != null ? type.equals(that.type) : that.type == null);
}
@Override
public int hashCode() {
int result = category;
result = 31 * result + (type != null ? type.hashCode() : 0);
return result;
}
// The Unknown category denotes a register type that hasn't been determined yet
public static final byte UNKNOWN = 0;
// The Uninit category is for registers that haven't been set yet. e.g. the non-parameter registers in a method
// start out as unint
public static final byte UNINIT = 1;
public static final byte NULL = 2;
public static final byte ONE = 3;
public static final byte BOOLEAN = 4;
public static final byte BYTE = 5;
public static final byte POS_BYTE = 6;
public static final byte SHORT = 7;
public static final byte POS_SHORT = 8;
public static final byte CHAR = 9;
public static final byte INTEGER = 10;
public static final byte FLOAT = 11;
public static final byte LONG_LO = 12;
public static final byte LONG_HI = 13;
public static final byte DOUBLE_LO = 14;
public static final byte DOUBLE_HI = 15;
// The UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
public static final byte UNINIT_REF = 16;
// The UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
// method is called
public static final byte UNINIT_THIS = 17;
public static final byte REFERENCE = 18;
// 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 incoming code path, but is a Reference type on another
// incomming code path. There is no register type that can hold either an Integer or a Reference.
public static final byte CONFLICTED = 19;
public static final String[] CATEGORY_NAMES = new String[] {
"Unknown",
"Uninit",
"Null",
"One",
"Boolean",
"Byte",
"PosByte",
"Short",
"PosShort",
"Char",
"Integer",
"Float",
"LongLo",
"LongHi",
"DoubleLo",
"DoubleHi",
"UninitRef",
"UninitThis",
"Reference",
"Conflicted"
};
//this table is used when merging register types. For example, if a particular register can be either a BYTE
//or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can
//could hold either type of value.
protected static byte[][] mergeTable =
{
/* UNKNOWN UNINIT NULL ONE, BOOLEAN BYTE POS_BYTE SHORT POS_SHORT CHAR INTEGER, FLOAT, LONG_LO LONG_HI DOUBLE_LO DOUBLE_HI UNINIT_REF UNINIT_THIS REFERENCE CONFLICTED*/
/*UNKNOWN*/ {UNKNOWN, UNINIT, NULL, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, LONG_LO, LONG_HI, DOUBLE_LO, DOUBLE_HI, UNINIT_REF, UNINIT_THIS,REFERENCE, CONFLICTED},
/*UNINIT*/ {UNINIT, UNINIT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*NULL*/ {NULL, CONFLICTED, NULL, BOOLEAN, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, REFERENCE, CONFLICTED},
/*ONE*/ {ONE, CONFLICTED, BOOLEAN, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*BOOLEAN*/ {BOOLEAN, CONFLICTED, BOOLEAN, BOOLEAN, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*BYTE*/ {BYTE, CONFLICTED, BYTE, BYTE, BYTE, BYTE, BYTE, SHORT, SHORT, INTEGER, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*POS_BYTE*/ {POS_BYTE, CONFLICTED, POS_BYTE, POS_BYTE, POS_BYTE, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*SHORT*/ {SHORT, CONFLICTED, SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, INTEGER, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*POS_SHORT*/ {POS_SHORT, CONFLICTED, POS_SHORT, POS_SHORT, POS_SHORT, SHORT, POS_SHORT, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*CHAR*/ {CHAR, CONFLICTED, CHAR, CHAR, CHAR, INTEGER, CHAR, INTEGER, CHAR, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*INTEGER*/ {INTEGER, CONFLICTED, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*FLOAT*/ {FLOAT, CONFLICTED, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*LONG_LO*/ {LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*LONG_HI*/ {LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*DOUBLE_LO*/ {DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*DOUBLE_HI*/ {DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*UNINIT_REF*/ {UNINIT_REF, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED},
/*UNINIT_THIS*/{UNINIT_THIS, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, UNINIT_THIS,CONFLICTED, CONFLICTED},
/*REFERENCE*/ {REFERENCE, CONFLICTED, 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, CONFLICTED}
};
public static final RegisterType UNKNOWN_TYPE = new RegisterType(UNKNOWN, null);
public static final RegisterType UNINIT_TYPE = new RegisterType(UNINIT, null);
public static final RegisterType NULL_TYPE = new RegisterType(NULL, null);
public static final RegisterType ONE_TYPE = new RegisterType(ONE, null);
public static final RegisterType BOOLEAN_TYPE = new RegisterType(BOOLEAN, null);
public static final RegisterType BYTE_TYPE = new RegisterType(BYTE, null);
public static final RegisterType POS_BYTE_TYPE = new RegisterType(POS_BYTE, null);
public static final RegisterType SHORT_TYPE = new RegisterType(SHORT, null);
public static final RegisterType POS_SHORT_TYPE = new RegisterType(POS_SHORT, null);
public static final RegisterType CHAR_TYPE = new RegisterType(CHAR, null);
public static final RegisterType INTEGER_TYPE = new RegisterType(INTEGER, null);
public static final RegisterType FLOAT_TYPE = new RegisterType(FLOAT, null);
public static final RegisterType LONG_LO_TYPE = new RegisterType(LONG_LO, null);
public static final RegisterType LONG_HI_TYPE = new RegisterType(LONG_HI, null);
public static final RegisterType DOUBLE_LO_TYPE = new RegisterType(DOUBLE_LO, null);
public static final RegisterType DOUBLE_HI_TYPE = new RegisterType(DOUBLE_HI, null);
public static final RegisterType CONFLICTED_TYPE = new RegisterType(CONFLICTED, null);
@Nonnull
public static RegisterType getWideRegisterType(@Nonnull CharSequence type, boolean firstRegister) {
switch (type.charAt(0)) {
case 'J':
if (firstRegister) {
return getRegisterType(LONG_LO, null);
} else {
return getRegisterType(LONG_HI, null);
}
case 'D':
if (firstRegister) {
return getRegisterType(DOUBLE_LO, null);
} else {
return getRegisterType(DOUBLE_HI, null);
}
default:
throw new ExceptionWithContext("Cannot use this method for narrow register type: %s", type);
}
}
@Nonnull
public static RegisterType getRegisterType(@Nonnull ClassPath classPath, @Nonnull CharSequence type) {
switch (type.charAt(0)) {
case 'Z':
return BOOLEAN_TYPE;
case 'B':
return BYTE_TYPE;
case 'S':
return SHORT_TYPE;
case 'C':
return CHAR_TYPE;
case 'I':
return INTEGER_TYPE;
case 'F':
return FLOAT_TYPE;
case 'J':
return LONG_LO_TYPE;
case 'D':
return DOUBLE_LO_TYPE;
case 'L':
case '[':
return getRegisterType(REFERENCE, classPath.getClass(type));
default:
throw new ExceptionWithContext("Invalid type: " + type);
}
}
@Nonnull
public static RegisterType getRegisterTypeForLiteral(int literalValue) {
if (literalValue < -32768) {
return INTEGER_TYPE;
}
if (literalValue < -128) {
return SHORT_TYPE;
}
if (literalValue < 0) {
return BYTE_TYPE;
}
if (literalValue == 0) {
return NULL_TYPE;
}
if (literalValue == 1) {
return ONE_TYPE;
}
if (literalValue < 128) {
return POS_BYTE_TYPE;
}
if (literalValue < 32768) {
return POS_SHORT_TYPE;
}
if (literalValue < 65536) {
return CHAR_TYPE;
}
return INTEGER_TYPE;
}
@Nonnull
public RegisterType merge(@Nonnull RegisterType other) {
if (other.equals(this)) {
return this;
}
byte mergedCategory = mergeTable[this.category][other.category];
TypeProto mergedType = null;
if (mergedCategory == REFERENCE) {
TypeProto type = this.type;
if (type != null) {
if (other.type != null) {
mergedType = type.getCommonSuperclass(other.type);
} else {
mergedType = type;
}
} else {
mergedType = other.type;
}
} else if (mergedCategory == UNINIT_REF || mergedCategory == UNINIT_THIS) {
if (this.category == UNKNOWN) {
return other;
}
assert other.category == UNKNOWN;
return this;
}
if (mergedType != null) {
if (mergedType.equals(this.type)) {
return this;
}
if (mergedType.equals(other.type)) {
return other;
}
}
return RegisterType.getRegisterType(mergedCategory, mergedType);
}
@Nonnull
public static RegisterType getRegisterType(byte category, @Nullable TypeProto typeProto) {
switch (category) {
case UNKNOWN:
return UNKNOWN_TYPE;
case UNINIT:
return UNINIT_TYPE;
case NULL:
return NULL_TYPE;
case ONE:
return ONE_TYPE;
case BOOLEAN:
return BOOLEAN_TYPE;
case BYTE:
return BYTE_TYPE;
case POS_BYTE:
return POS_BYTE_TYPE;
case SHORT:
return SHORT_TYPE;
case POS_SHORT:
return POS_SHORT_TYPE;
case CHAR:
return CHAR_TYPE;
case INTEGER:
return INTEGER_TYPE;
case FLOAT:
return FLOAT_TYPE;
case LONG_LO:
return LONG_LO_TYPE;
case LONG_HI:
return LONG_HI_TYPE;
case DOUBLE_LO:
return DOUBLE_LO_TYPE;
case DOUBLE_HI:
return DOUBLE_HI_TYPE;
case CONFLICTED:
return CONFLICTED_TYPE;
}
return new RegisterType(category, typeProto);
}
}