blob: ed67732ae3b859eca7bb8152c0e64eb12937d13a [file] [log] [blame]
/*
* [The "BSD licence"]
* Copyright (c) 2010 Ben Gruver (JesusFreke)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.dexlib.Code.Analysis;
import org.jf.dexlib.TypeIdItem;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import static org.jf.dexlib.Code.Analysis.ClassPath.ClassDef;
public class RegisterType {
private final static HashMap<RegisterType, RegisterType> internedRegisterTypes =
new HashMap<RegisterType, RegisterType>();
public final Category category;
public final ClassDef type;
private RegisterType(Category category, ClassDef type) {
assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
type != null) ||
((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
type == null);
this.category = category;
this.type = type;
}
@Override
public String toString() {
return "(" + category.name() + (type==null?"":("," + type.getClassType())) + ")";
}
public void writeTo(Writer writer) throws IOException {
writer.write('(');
writer.write(category.name());
if (type != null) {
writer.write(',');
writer.write(type.getClassType());
}
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;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return true;
}
@Override
public int hashCode() {
int result = category.hashCode();
result = 31 * result + (type != null ? type.hashCode() : 0);
return result;
}
public static enum Category {
//the Unknown category denotes a register type that hasn't been determined yet
Unknown,
Uninit,
Null,
One,
Boolean,
Byte,
PosByte,
Short,
PosShort,
Char,
Integer,
Float,
LongLo,
LongHi,
DoubleLo,
DoubleHi,
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
UninitRef,
//the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
//method is called
UninitThis,
Reference,
//This is used when there are multiple incoming execution paths that have incompatible register types. For
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another
//incomming code path. There is no register type that can hold either an Integer or a 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 Category[][] mergeTable =
{
/* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/
/*Unknown*/ {Unknown, Uninit, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, 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, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*One*/ {One, Conflicted, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Boolean*/ {Boolean, Conflicted, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, 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},
/*PosByte*/ {PosByte, Conflicted, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, 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},
/*PosShort*/ {PosShort, Conflicted, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, 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},
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, 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}
};
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
//example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an
//array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()]
//Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
protected static boolean[][] assigmentTable =
{
/* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Uninit*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Null*/ {false, false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false},
/*One*/ {false, false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Boolean*/ {false, false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Byte*/ {false, false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false},
/*PosByte*/ {false, false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Short*/ {false, false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false},
/*PosShort*/ {false, false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Char*/ {false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false},
/*Integer*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*Float*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
/*----------*/
/*value type*/
};
}
public static RegisterType getRegisterTypeForType(String type) {
switch (type.charAt(0)) {
case 'V':
throw new ValidationException("The V type can only be used as a method return type");
case 'Z':
return getRegisterType(Category.Boolean, null);
case 'B':
return getRegisterType(Category.Byte, null);
case 'S':
return getRegisterType(Category.Short, null);
case 'C':
return getRegisterType(Category.Char, null);
case 'I':
return getRegisterType(Category.Integer, null);
case 'F':
return getRegisterType(Category.Float, null);
case 'J':
return getRegisterType(Category.LongLo, null);
case 'D':
return getRegisterType(Category.DoubleLo, null);
case 'L':
case '[':
return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
default:
throw new RuntimeException("Invalid type: " + type);
}
}
public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
return getRegisterTypeForType(typeIdItem.getTypeDescriptor());
}
public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
if (typeIdItem.getRegisterCount() == 1) {
throw new RuntimeException("Cannot use this method for non-wide register type: " +
typeIdItem.getTypeDescriptor());
}
switch (typeIdItem.getTypeDescriptor().charAt(0)) {
case 'J':
if (firstRegister) {
return getRegisterType(Category.LongLo, null);
} else {
return getRegisterType(Category.LongHi, null);
}
case 'D':
if (firstRegister) {
return getRegisterType(Category.DoubleLo, null);
} else {
return getRegisterType(Category.DoubleHi, null);
}
default:
throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor());
}
}
public static RegisterType getRegisterTypeForLiteral(long literalValue) {
if (literalValue < -32768) {
return getRegisterType(Category.Integer, null);
}
if (literalValue < -128) {
return getRegisterType(Category.Short, null);
}
if (literalValue < 0) {
return getRegisterType(Category.Byte, null);
}
if (literalValue == 0) {
return getRegisterType(Category.Null, null);
}
if (literalValue == 1) {
return getRegisterType(Category.One, null);
}
if (literalValue < 128) {
return getRegisterType(Category.PosByte, null);
}
if (literalValue < 32768) {
return getRegisterType(Category.PosShort, null);
}
if (literalValue < 65536) {
return getRegisterType(Category.Char, null);
}
return getRegisterType(Category.Integer, null);
}
public RegisterType merge(RegisterType type) {
if (type == null || type == this) {
return this;
}
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
ClassDef mergedType = null;
if (mergedCategory == Category.Reference) {
if (this.type instanceof ClassPath.UnresolvedClassDef ||
type.type instanceof ClassPath.UnresolvedClassDef) {
mergedType = ClassPath.getUnresolvedObjectClassDef();
} else {
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
}
} else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
if (this.category == Category.Unknown) {
return type;
}
assert type.category == Category.Unknown;
return this;
}
return RegisterType.getRegisterType(mergedCategory, mergedType);
}
public boolean canBeAssignedTo(RegisterType slotType) {
if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) {
if (this.category == Category.Reference && slotType.category == Category.Reference) {
if (!slotType.type.isInterface()) {
return this.type.extendsClass(slotType.type);
}
//for verification, we assume all objects implement all interfaces, so we don't verify the type if
//slotType is an interface
}
return true;
}
return false;
}
public static RegisterType getUnitializedReference(ClassDef classType) {
//We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance
//is used to track a specific uninitialized reference, so that if multiple registers contain the same
//uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate
//<init> is invoked
return new RegisterType(Category.UninitRef, classType);
}
public static RegisterType getRegisterType(Category category, ClassDef classType) {
RegisterType newRegisterType = new RegisterType(category, classType);
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
if (internedRegisterType == null) {
internedRegisterTypes.put(newRegisterType, newRegisterType);
return newRegisterType;
}
return internedRegisterType;
}
}