some (very) basic support for pointer / reference types

This commit is contained in:
Manu Sridharan 2012-08-21 16:56:18 -07:00
parent 800203a71f
commit bab4777e4c
10 changed files with 195 additions and 83 deletions

View File

@ -35,6 +35,7 @@ import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import static com.ibm.wala.types.TypeName.*;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
@ -227,8 +228,17 @@ public abstract class AbstractReflectionInterpreter implements SSAContextInterpr
if (t.isArrayType()) {
// for now, just allocate an array of size 1 in each dimension.
int dim = t.getDimensionality();
int[] extents = new int[dim];
int dims = 0;
int dim = t.getDerivedMask();
if ((dim&ElementMask) == PrimitiveMask) {
dim >>= 2;
}
while ((dim&ElementMask) == ArrayMask) {
dims++;
dim >>=2;
}
int[] extents = new int[dims];
Arrays.fill(extents, 1);
SSANewInstruction a = insts.NewInstruction(alloc, ref, extents);
addInstruction(t, a, true);

View File

@ -18,6 +18,7 @@ import java.util.Set;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
@ -155,7 +156,7 @@ public class CloneInterpreter implements SSAContextInterpreter {
if (klass.isArrayClass()) {
int length = nextLocal++;
statements.add(insts.ArrayLengthInstruction(length, 1));
int[] sizes = new int[klass.getReference().getDimensionality()];
int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
Arrays.fill(sizes, length);
N = insts.NewInstruction(retValue, ref, sizes);
} else {

View File

@ -25,6 +25,7 @@ import com.ibm.wala.analysis.typeInference.SetType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
@ -528,7 +529,7 @@ class FactoryBypassInterpreter extends AbstractReflectionInterpreter {
NewSiteReference ref = NewSiteReference.make(getNewSiteForType(T), T);
SSANewInstruction a = null;
if (T.isArrayType()) {
int[] sizes = new int[T.getDimensionality()];
int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
initValueNumberForConstantOne();
Arrays.fill(sizes, valueNumberForConstantOne);
a = insts.NewInstruction(i, ref, sizes);

View File

@ -19,6 +19,7 @@ import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import static com.ibm.wala.types.TypeName.*;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
@ -234,7 +235,17 @@ public class ArrayClass implements IClass, Constants {
}
public int getDimensionality() {
return getReference().getDimensionality();
int mask = getReference().getDerivedMask();
if ((mask&PrimitiveMask) == PrimitiveMask) {
mask >>= 2;
}
int dims = 0;
while ((mask&ArrayMask) == ArrayMask) {
mask >>= 2;
dims++;
}
assert dims>0;
return dims;
}
/**

View File

@ -16,6 +16,7 @@ import java.util.Iterator;
import java.util.Map;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
@ -174,7 +175,7 @@ public abstract class AbstractRootMethod extends SyntheticMethod {
int instance = nextLocal++;
NewSiteReference ref = NewSiteReference.make(statements.size(), T);
assert T.isArrayType();
assert T.getDimensionality() == 1;
assert ((ArrayClass)cha.lookupClass(T)).getDimensionality() == 1;
int[] sizes = new int[1];
Arrays.fill(sizes, getValueNumberForIntConstant(length));
SSANewInstruction result = insts.NewInstruction(instance, ref, sizes);
@ -206,7 +207,7 @@ public abstract class AbstractRootMethod extends SyntheticMethod {
if (T.isReferenceType()) {
NewSiteReference ref = NewSiteReference.make(statements.size(), T);
if (T.isArrayType()) {
int[] sizes = new int[T.getDimensionality()];
int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
Arrays.fill(sizes, getValueNumberForIntConstant(1));
result = insts.NewInstruction(instance, ref, sizes);
} else {
@ -229,7 +230,7 @@ public abstract class AbstractRootMethod extends SyntheticMethod {
int alloc = nextLocal++;
SSANewInstruction ni = null;
if (e.isArrayType()) {
int[] sizes = new int[T.getDimensionality()];
int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
Arrays.fill(sizes, getValueNumberForIntConstant(1));
ni = insts.NewInstruction(alloc, n, sizes);
} else {

View File

@ -15,6 +15,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.dataflow.graph.BitVectorSolver;
@ -259,7 +260,7 @@ public class ModRef {
@Override
public void visitNew(SSANewInstruction instruction) {
if (instruction.getConcreteType().isArrayType()) {
int dim = instruction.getConcreteType().getDimensionality();
int dim = ((ArrayClass)n.getClassHierarchy().lookupClass(instruction.getConcreteType())).getDimensionality();
if (dim > 1) {
// we need to handle the top-level allocation, just like the 1D case
InstanceKey ik = h.getInstanceKeyForAllocation(n, instruction.getNewSite());

View File

@ -45,6 +45,7 @@ import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import static com.ibm.wala.types.TypeName.*;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
@ -515,7 +516,11 @@ public class XMLMethodSummaryReader implements BytecodeConstants {
Assertions.productionAssertion(size != null);
Integer sNumber = symbolTable.get(size);
Assertions.productionAssertion(sNumber != null);
Assertions.productionAssertion(type.getDimensionality() == 1);
Assertions.productionAssertion(
// array of objects
type.getDerivedMask()==ArrayMask ||
// array of primitives
type.getDerivedMask()==((ArrayMask<<2)|PrimitiveMask));
a = insts.NewInstruction(defNum, ref, new int[] { sNumber.intValue() });
} else {
a = insts.NewInstruction(defNum, ref);

View File

@ -27,6 +27,14 @@ import com.ibm.wala.util.strings.StringStuff;
*/
public final class TypeName implements Serializable {
public static final byte ArrayMask = 0x01;
public static final byte PointerMask = 0x02;
public static final byte ReferenceMask = 0x03;
public static final byte PrimitiveMask = 0x04;
public static final byte ElementMask = 0x07;
public static final byte ElementBits = 3;
/* Serial version */
private static final long serialVersionUID = -3256390509887654326L;
@ -54,12 +62,17 @@ public final class TypeName implements Serializable {
Atom className = Atom.findOrCreate(StringStuff.parseForClass(name, start, length));
ImmutableByteArray p = StringStuff.parseForPackage(name, start, length);
Atom packageName = (p == null) ? null : Atom.findOrCreate(p);
short dim = StringStuff.parseForArrayDimensionality(name, start, length);
int dim = StringStuff.parseForArrayDimensionality(name, start, length);
boolean innermostPrimitive = StringStuff.classIsPrimitive(name, start, length);
if (innermostPrimitive && (dim == 0)) {
dim = -1;
if (innermostPrimitive) {
if (dim == 0) {
dim = -1;
} else {
dim <<= ElementBits;
dim |= PrimitiveMask;
}
}
TypeNameKey t = new TypeNameKey(packageName, className, dim, innermostPrimitive);
TypeNameKey t = new TypeNameKey(packageName, className, dim);
return findOrCreate(t);
}
@ -82,12 +95,12 @@ public final class TypeName implements Serializable {
if (className == null) {
throw new IllegalArgumentException("null className");
}
TypeNameKey T = new TypeNameKey(packageName, className, (short) 0, false);
TypeNameKey T = new TypeNameKey(packageName, className, 0);
return findOrCreate(T);
}
private static TypeName findOrCreate(Atom packageName, Atom className, short dim, boolean innermostPrimitive) {
TypeNameKey T = new TypeNameKey(packageName, className, dim, innermostPrimitive);
public static TypeName findOrCreate(Atom packageName, Atom className, int dim) {
TypeNameKey T = new TypeNameKey(packageName, className, dim);
return findOrCreate(T);
}
@ -141,29 +154,52 @@ public final class TypeName implements Serializable {
* @return the name of the array element type for an array
*/
public TypeName parseForArrayElementName() {
short newDim = (short) (key.dim - 1);
if (newDim == 0 && key.innermostPrimitive) {
newDim = -1;
int newDim;
if ((key.dim&ElementMask) == PrimitiveMask) {
int tmpDim = key.dim>>(2*ElementBits);
if (tmpDim == 0) {
newDim = -1;
} else {
newDim = (tmpDim<<ElementBits) | PrimitiveMask;
}
} else {
newDim = key.dim>>ElementBits;
}
return findOrCreate(key.packageName, key.className, newDim, key.innermostPrimitive);
return findOrCreate(key.packageName, key.className, newDim);
}
/**
* @return a type name that represents an array of this element type
*/
public TypeName getArrayTypeForElementType() {
short newDim = (short) (key.dim + 1);
if (newDim == 0) {
newDim = 1;
private TypeName getDerivedTypeForElementType(byte mask) {
int newDim;
if (key.dim == -1) {
newDim = mask<<ElementBits | PrimitiveMask;
} else if ((key.dim & ElementMask) == PrimitiveMask) {
newDim = (((key.dim & ~ElementMask) | mask)<<ElementBits) | PrimitiveMask;
} else {
newDim = key.dim<<ElementBits | mask;
}
return findOrCreate(key.packageName, key.className, newDim, key.innermostPrimitive);
return findOrCreate(key.packageName, key.className, newDim);
}
public TypeName getArrayTypeForElementType() {
return getDerivedTypeForElementType(ArrayMask);
}
public TypeName getPointerTypeForElementType() {
return getDerivedTypeForElementType(PointerMask);
}
public TypeName getReferenceTypeForElementType() {
return getDerivedTypeForElementType(ReferenceMask);
}
/**
* @return the dimensionality of the type. By convention, class types have dimensionality 0, primitves -1, and arrays the number
* @return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays the number
* of [ in their descriptor.
*/
public final int getDimensionality() {
public final int getDerivedMask() {
return key.dim;
}
@ -192,8 +228,8 @@ public final class TypeName implements Serializable {
* Return the innermost element type reference for an array
*/
public final TypeName getInnermostElementType() {
short newDim = key.innermostPrimitive ? (short) -1 : 0;
return findOrCreate(key.packageName, key.className, newDim, key.innermostPrimitive);
short newDim = ((key.dim&ElementMask) == PrimitiveMask) ? (short) -1 : 0;
return findOrCreate(key.packageName, key.className, newDim);
}
/**
@ -211,31 +247,26 @@ public final class TypeName implements Serializable {
private final Atom className;
/**
* Dimensionality: -1 => primitive 0 => class >0 => array
* Dimensionality: -1 => primitive
* 0 => class
* >0 => mask of levels of array, reference, pointer
*/
private final short dim;
/**
* Is the innermost element type a primitive? TODO: encode in dim?
*/
private final boolean innermostPrimitive;
private final int dim;
/**
* This should be the only constructor
*/
private TypeNameKey(Atom packageName, Atom className, short dim, boolean innermostPrimitive) {
private TypeNameKey(Atom packageName, Atom className, int dim) {
this.packageName = packageName;
this.className = className;
this.dim = dim;
this.innermostPrimitive = innermostPrimitive;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TypeNameKey) {
TypeNameKey other = (TypeNameKey) obj;
return className == other.className && packageName == other.packageName && dim == other.dim
&& innermostPrimitive == other.innermostPrimitive;
return className == other.className && packageName == other.packageName && dim == other.dim;
} else {
return false;
}
@ -246,7 +277,7 @@ public final class TypeName implements Serializable {
*/
@Override
public int hashCode() {
int result = className.hashCode() * 5009 + dim * 5011 + (innermostPrimitive ? 5021 : 5023);
int result = className.hashCode() * 5009 + dim * 5011;
if (packageName != null) {
result += packageName.hashCode();
}
@ -256,14 +287,7 @@ public final class TypeName implements Serializable {
@Override
public String toString() {
StringBuffer result = new StringBuffer();
for (short i = 0; i < dim; i++) {
result.append("[");
}
if (!innermostPrimitive) {
result.append("L");
} else if (packageName != null && innermostPrimitive) {
result.append("P");
}
toStringPrefix(result);
if (packageName != null) {
result.append(packageName.toString());
@ -274,17 +298,34 @@ public final class TypeName implements Serializable {
return result.toString();
}
private void toStringPrefix(StringBuffer result) {
boolean isPrimitive = (dim==-1) || (dim&ElementMask)==PrimitiveMask;
if (dim != -1) {
for (int d = (dim&ElementMask) == PrimitiveMask? dim>>ElementBits: dim; d != 0; d>>=ElementBits) {
switch (d&ElementMask) {
case ArrayMask:
result.append("[");
break;
case PointerMask:
result.append("*");
break;
case ReferenceMask:
result.append("&");
break;
}
}
}
if (!isPrimitive) {
result.append("L");
} else if (packageName != null && isPrimitive) {
result.append("P");
}
}
public String toUnicodeString() {
try {
StringBuffer result = new StringBuffer();
for (short i = 0; i < dim; i++) {
result.append("[");
}
if (!innermostPrimitive) {
result.append("L");
} else if (packageName != null && innermostPrimitive) {
result.append("P");
}
toStringPrefix(result);
if (packageName != null) {
result.append(packageName.toUnicodeString());

View File

@ -318,6 +318,10 @@ public final class TypeReference implements Serializable {
public final static byte ArrayTypeCode = '[';
public final static byte PointerTypeCode = '*';
public final static byte ReferenceTypeCode = '&';
// TODO! the following two are unsound hacks; kill them.
final static TypeName NullName = TypeName.string2TypeName("null");
@ -412,6 +416,30 @@ public final class TypeReference implements Serializable {
}
}
public static TypeReference findOrCreateReferenceTo(TypeReference t) {
if (t == null) {
throw new IllegalArgumentException("t is null");
}
TypeName name = t.getName();
if (t.isPrimitiveType()) {
return findOrCreate(ClassLoaderReference.Primordial, name.getReferenceTypeForElementType());
} else {
return findOrCreate(t.getClassLoader(), name.getReferenceTypeForElementType());
}
}
public static TypeReference findOrCreatePointerTo(TypeReference t) {
if (t == null) {
throw new IllegalArgumentException("t is null");
}
TypeName name = t.getName();
if (t.isPrimitiveType()) {
return findOrCreate(ClassLoaderReference.Primordial, name.getPointerTypeForElementType());
} else {
return findOrCreate(t.getClassLoader(), name.getPointerTypeForElementType());
}
}
/**
* NB: All type names should use '/' and not '.' as a separator. eg. Ljava/lang/Class
*
@ -456,8 +484,8 @@ public final class TypeReference implements Serializable {
* Return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays
* the number of [ in their descriptor.
*/
public final int getDimensionality() {
return name.getDimensionality();
public final int getDerivedMask() {
return name.getDerivedMask();
}
/**

View File

@ -10,6 +10,11 @@
*******************************************************************************/
package com.ibm.wala.util.strings;
import static com.ibm.wala.types.TypeName.*;
import static com.ibm.wala.types.TypeReference.ArrayTypeCode;
import static com.ibm.wala.types.TypeReference.PointerTypeCode;
import static com.ibm.wala.types.TypeReference.ReferenceTypeCode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@ -263,28 +268,19 @@ public class StringStuff {
continue;
}
case TypeReference.ArrayTypeCode: {
case TypeReference.ArrayTypeCode:
case TypeReference.PointerTypeCode:
case TypeReference.ReferenceTypeCode: {
int off = i - 1;
while (b.get(i) == TypeReference.ArrayTypeCode) {
++i;
while (StringStuff.isTypeCodeChar(b, i)) {
++i;
}
TypeName T = null;
byte c = b.get(i++);
if (c == TypeReference.ClassTypeCode) {
if (c == TypeReference.ClassTypeCode || c == TypeReference.OtherPrimitiveTypeCode) {
while (b.get(i++) != ';')
;
T = TypeName.findOrCreate(b, off, i - off - 1);
} else if (c == TypeReference.OtherPrimitiveTypeCode) {
int typeOff = i;
while (b.get(i++) != ';')
;
T = l.lookupPrimitiveType(new String(b.substring(typeOff, i - typeOff - 1)));
while (--typeOff > off) {
T = T.getArrayTypeForElementType();
}
} else {
T = TypeName.findOrCreate(b, off, i - off);
}
@ -309,6 +305,12 @@ public class StringStuff {
}
}
public static boolean isTypeCodeChar(ImmutableByteArray name, int i) {
return name.b[i] == TypeReference.ArrayTypeCode ||
name.b[i] == TypeReference.PointerTypeCode ||
name.b[i] == TypeReference.ReferenceTypeCode;
}
/**
* Given that name[start:start+length] is a Type name in JVM format, parse it for the package
*
@ -329,7 +331,10 @@ public class StringStuff {
if (lastSlash == -1) {
return null;
}
short dim = parseForArrayDimensionality(name, start, length);
short dim = 0;
while (isTypeCodeChar(name, start+dim)) {
dim++;
}
return new ImmutableByteArray(name.b, start + 1 + dim, lastSlash - start - 1 - dim);
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("invalid name " + name + " start: " + start + " length: " + length);
@ -366,7 +371,7 @@ public class StringStuff {
}
try {
if (parseForPackage(name, start, length) == null) {
while (name.b[start] == '[') {
while (isTypeCodeChar(name, start)) {
start++;
length--;
}
@ -410,22 +415,30 @@ public class StringStuff {
* @return dimensionality - something like "1" or "2"
* @throws IllegalArgumentException if b == null
*/
public static short parseForArrayDimensionality(ImmutableByteArray b, int start, int length) throws IllegalArgumentException,
public static int parseForArrayDimensionality(ImmutableByteArray b, int start, int length) throws IllegalArgumentException,
IllegalArgumentException {
if (b == null) {
throw new IllegalArgumentException("b == null");
}
try {
int code = 0;
for (int i = start; i < start + length; ++i) {
if (b.b[i] != '[') {
return (short) (i - start);
if (isTypeCodeChar(b, i)) {
code <<= ElementBits;
switch (b.b[i]) {
case ArrayTypeCode: code |= ArrayMask; break;
case PointerTypeCode: code |= PointerMask; break;
case ReferenceTypeCode: code |= ReferenceMask; break;
default:
throw new IllegalArgumentException("ill-formed array descriptor " + b);
}
}
}
return code;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("ill-formed array descriptor " + b);
}
throw new IllegalArgumentException("ill-formed array descriptor " + b);
}
/**
@ -443,7 +456,7 @@ public class StringStuff {
try {
int i = start;
for (; i < start + length; ++i) {
if (b.b[i] != '[') {
if (! isTypeCodeChar(b, i)) {
break;
}
}
@ -477,7 +490,7 @@ public class StringStuff {
throw new IllegalArgumentException("name is null");
}
try {
while (length > 0 && name.b[start] == '[') {
while (length > 0 && isTypeCodeChar(name, start)) {
start++;
length--;
}