810 lines
23 KiB
Java
810 lines
23 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.analysis.typeInference;
|
|
|
|
import java.util.Collection;
|
|
import java.util.Iterator;
|
|
|
|
import com.ibm.wala.classLoader.IClass;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.classLoader.Language;
|
|
import com.ibm.wala.dataflow.ssa.SSAInference;
|
|
import com.ibm.wala.fixedpoint.impl.NullaryOperator;
|
|
import com.ibm.wala.fixpoint.AbstractOperator;
|
|
import com.ibm.wala.fixpoint.FixedPointConstants;
|
|
import com.ibm.wala.fixpoint.IVariable;
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
|
import com.ibm.wala.ssa.IR;
|
|
import com.ibm.wala.ssa.IVisitorWithAddresses;
|
|
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSAAddressOfInstruction;
|
|
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
|
|
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
|
|
import com.ibm.wala.ssa.SSABinaryOpInstruction;
|
|
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
|
|
import com.ibm.wala.ssa.SSACheckCastInstruction;
|
|
import com.ibm.wala.ssa.SSAComparisonInstruction;
|
|
import com.ibm.wala.ssa.SSAConversionInstruction;
|
|
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
|
|
import com.ibm.wala.ssa.SSAGetInstruction;
|
|
import com.ibm.wala.ssa.SSAInstanceofInstruction;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSALoadIndirectInstruction;
|
|
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
|
|
import com.ibm.wala.ssa.SSANewInstruction;
|
|
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
import com.ibm.wala.ssa.SSAPiInstruction;
|
|
import com.ibm.wala.ssa.SSAStoreIndirectInstruction;
|
|
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
|
|
import com.ibm.wala.ssa.SymbolTable;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.CancelException;
|
|
import com.ibm.wala.util.CancelRuntimeException;
|
|
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
|
|
/**
|
|
* This class performs intraprocedural type propagation on an SSA IR.
|
|
*/
|
|
public class TypeInference extends SSAInference<TypeVariable> implements FixedPointConstants {
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
public static TypeInference make(IR ir, boolean doPrimitives) {
|
|
return new TypeInference(ir, doPrimitives);
|
|
}
|
|
|
|
/**
|
|
* The governing SSA form
|
|
*/
|
|
final protected IR ir;
|
|
|
|
/**
|
|
* The governing class hierarchy
|
|
*/
|
|
final protected IClassHierarchy cha;
|
|
|
|
final protected Language language;
|
|
|
|
/**
|
|
* A singleton instance of the phi operator.
|
|
*/
|
|
private final static AbstractOperator<TypeVariable> phiOp = new PhiOperator();
|
|
|
|
private final static AbstractOperator<TypeVariable> primitivePropagateOp = new PrimitivePropagateOperator();
|
|
|
|
/**
|
|
* A cone type for java.lang.Object
|
|
*/
|
|
protected final TypeAbstraction BOTTOM;
|
|
|
|
/**
|
|
* A singleton instance of the pi operator.
|
|
*/
|
|
private final static PiOperator piOp = new PiOperator();
|
|
|
|
/**
|
|
* should type inference track primitive types?
|
|
*/
|
|
protected final boolean doPrimitives;
|
|
|
|
private boolean solved = false;
|
|
|
|
protected TypeInference(IR ir, boolean doPrimitives) {
|
|
if (ir == null) {
|
|
throw new IllegalArgumentException("ir is null");
|
|
}
|
|
this.language = ir.getMethod().getDeclaringClass().getClassLoader().getLanguage();
|
|
this.cha = ir.getMethod().getDeclaringClass().getClassHierarchy();
|
|
this.ir = ir;
|
|
this.doPrimitives = doPrimitives;
|
|
this.BOTTOM = new ConeType(cha.getRootClass());
|
|
initialize();
|
|
solve();
|
|
}
|
|
|
|
public boolean solve() {
|
|
return solve(null);
|
|
}
|
|
|
|
@Override
|
|
public boolean solve(IProgressMonitor monitor) {
|
|
try {
|
|
if (solved) {
|
|
return false;
|
|
} else {
|
|
boolean result = super.solve(null);
|
|
solved = true;
|
|
return result;
|
|
}
|
|
} catch (CancelException e) {
|
|
throw new CancelRuntimeException(e);
|
|
}
|
|
}
|
|
|
|
protected void initialize() {
|
|
init(ir, this.new TypeVarFactory(), this.new TypeOperatorFactory());
|
|
}
|
|
|
|
@Override
|
|
protected void initializeVariables() {
|
|
|
|
if (DEBUG) {
|
|
System.err.println("initializeVariables " + ir.getMethod());
|
|
}
|
|
|
|
int[] parameterValueNumbers = ir.getParameterValueNumbers();
|
|
for (int i = 0; i < parameterValueNumbers.length; i++) {
|
|
TypeVariable v = getVariable(parameterValueNumbers[i]);
|
|
TypeReference t = ir.getParameterType(i);
|
|
|
|
if (DEBUG) {
|
|
System.err.println("parameter " + parameterValueNumbers[i] + " " + t);
|
|
}
|
|
|
|
if (t.isReferenceType()) {
|
|
IClass klass = cha.lookupClass(t);
|
|
if (DEBUG) {
|
|
System.err.println("klass " + klass);
|
|
}
|
|
if (klass != null) {
|
|
v.setType(new ConeType(klass));
|
|
} else {
|
|
// give up .. default to java.lang.Object (BOTTOM)
|
|
v.setType(BOTTOM);
|
|
}
|
|
} else if (doPrimitives) {
|
|
v.setType(language.getPrimitive(t));
|
|
}
|
|
}
|
|
|
|
SymbolTable st = ir.getSymbolTable();
|
|
if (st != null) {
|
|
for (int i = 0; i <= st.getMaxValueNumber(); i++) {
|
|
if (st.isConstant(i)) {
|
|
TypeVariable v = getVariable(i);
|
|
v.setType(getConstantType(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Iterator<SSAInstruction> it = ir.iterateNormalInstructions(); it.hasNext();) {
|
|
SSAInstruction s = it.next();
|
|
if (s instanceof SSAAbstractInvokeInstruction) {
|
|
SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) s;
|
|
TypeVariable v = getVariable(call.getException());
|
|
Collection<TypeReference> defaultExceptions = call.getExceptionTypes();
|
|
if (defaultExceptions.size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
Iterator<TypeReference> types = defaultExceptions.iterator();
|
|
TypeReference t = types.next();
|
|
IClass klass = cha.lookupClass(t);
|
|
if (klass == null) {
|
|
v.setType(BOTTOM);
|
|
} else {
|
|
v.setType(new PointType(klass));
|
|
}
|
|
|
|
while(types.hasNext()) {
|
|
t = types.next();
|
|
klass = cha.lookupClass(t);
|
|
if (klass != null) {
|
|
v.setType(v.getType().meet(new PointType(klass)));
|
|
}
|
|
}
|
|
|
|
IMethod m = cha.resolveMethod(call.getDeclaredTarget());
|
|
if (m != null) {
|
|
TypeReference[] x = null;
|
|
try {
|
|
x = m.getDeclaredExceptions();
|
|
} catch (InvalidClassFileException e) {
|
|
e.printStackTrace();
|
|
Assertions.UNREACHABLE();
|
|
} catch (UnsupportedOperationException e) {
|
|
x = new TypeReference[]{ language.getThrowableType() };
|
|
}
|
|
if (x != null) {
|
|
for (int i = 0; i < x.length; i++) {
|
|
TypeReference tx = x[i];
|
|
IClass tc = cha.lookupClass(tx);
|
|
if (tc != null) {
|
|
v.setType(v.getType().meet(new ConeType(tc)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void initializeWorkList() {
|
|
addAllStatementsToWorkList();
|
|
}
|
|
|
|
/**
|
|
* An operator which initializes a type to a declared type.
|
|
*/
|
|
protected static final class DeclaredTypeOperator extends NullaryOperator<TypeVariable> {
|
|
private final TypeAbstraction type;
|
|
|
|
public DeclaredTypeOperator(TypeAbstraction type) {
|
|
assert type != null;
|
|
this.type = type;
|
|
}
|
|
|
|
/**
|
|
* Note that we need evaluate this operator at most once
|
|
*/
|
|
@Override
|
|
public byte evaluate(TypeVariable lhs) {
|
|
if (lhs.type.equals(type)) {
|
|
return NOT_CHANGED_AND_FIXED;
|
|
} else {
|
|
lhs.setType(type);
|
|
return CHANGED_AND_FIXED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see java.lang.Object#toString()
|
|
*/
|
|
@Override
|
|
public String toString() {
|
|
return "delared type := " + type;
|
|
}
|
|
|
|
public static boolean isNullary() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 9931 * type.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (o instanceof DeclaredTypeOperator) {
|
|
DeclaredTypeOperator d = (DeclaredTypeOperator) o;
|
|
return type.equals(d.type);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final class PhiOperator extends AbstractOperator<TypeVariable> {
|
|
|
|
private PhiOperator() {
|
|
}
|
|
|
|
/**
|
|
* TODO: work on efficiency shortcuts for this.
|
|
*/
|
|
@Override
|
|
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
|
|
|
if (DEBUG) {
|
|
System.err.print("PhiOperator.meet " + lhs + " ");
|
|
for (IVariable v : rhs) {
|
|
System.err.print(v + " ");
|
|
}
|
|
System.err.println();
|
|
}
|
|
|
|
TypeAbstraction lhsType = lhs.getType();
|
|
TypeAbstraction meet = TypeAbstraction.TOP;
|
|
for (int i = 0; i < rhs.length; i++) {
|
|
if (rhs[i] != null && rhs[i].getType() != null) {
|
|
TypeVariable r = rhs[i];
|
|
meet = meet.meet(r.getType());
|
|
}
|
|
}
|
|
if (lhsType.equals(meet)) {
|
|
return NOT_CHANGED;
|
|
} else {
|
|
lhs.setType(meet);
|
|
return CHANGED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "phi meet";
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 9929;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return (o instanceof PhiOperator);
|
|
}
|
|
}
|
|
|
|
private static final class PiOperator extends AbstractOperator<TypeVariable> {
|
|
|
|
private PiOperator() {
|
|
}
|
|
|
|
/**
|
|
* TODO: work on efficiency shortcuts for this.
|
|
*/
|
|
@Override
|
|
public byte evaluate(TypeVariable lhs, TypeVariable[] rhsOperands) {
|
|
TypeAbstraction lhsType = lhs.getType();
|
|
|
|
TypeVariable rhs = rhsOperands[0];
|
|
TypeAbstraction rhsType = rhs.getType();
|
|
|
|
if (lhsType.equals(rhsType)) {
|
|
return NOT_CHANGED;
|
|
} else {
|
|
lhs.setType(rhsType);
|
|
return CHANGED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "pi";
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 9929 * 13;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return (o instanceof PiOperator);
|
|
}
|
|
}
|
|
|
|
protected static class PrimitivePropagateOperator extends AbstractOperator<TypeVariable> {
|
|
|
|
protected PrimitivePropagateOperator() {
|
|
}
|
|
|
|
@Override
|
|
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
|
TypeAbstraction lhsType = lhs.getType();
|
|
TypeAbstraction meet = TypeAbstraction.TOP;
|
|
for (int i = 0; i < rhs.length; i++) {
|
|
if (rhs[i] != null && rhs[i].getType() != null) {
|
|
TypeVariable r = rhs[i];
|
|
meet = meet.meet(r.getType());
|
|
}
|
|
}
|
|
if (lhsType.equals(meet)) {
|
|
return NOT_CHANGED;
|
|
} else {
|
|
lhs.setType(meet);
|
|
return CHANGED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "propagate";
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 99292;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return o != null && o.getClass().equals(getClass());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This operator will extract the element type from an arrayref in an array access instruction
|
|
*
|
|
* TODO: why isn't this a nullary operator?
|
|
*/
|
|
private final class GetElementType extends AbstractOperator<TypeVariable> {
|
|
private final SSAArrayLoadInstruction load;
|
|
|
|
GetElementType(SSAArrayLoadInstruction load) {
|
|
this.load = load;
|
|
}
|
|
|
|
@Override
|
|
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
|
TypeAbstraction arrayType = getType(load.getArrayRef());
|
|
if (arrayType == null || arrayType.equals(TypeAbstraction.TOP)) {
|
|
return NOT_CHANGED;
|
|
}
|
|
TypeReference elementType = null;
|
|
|
|
if (arrayType instanceof PointType) {
|
|
elementType = ((PointType) arrayType).getType().getReference().getArrayElementType();
|
|
} else if (arrayType instanceof ConeType) {
|
|
elementType = ((ConeType) arrayType).getType().getReference().getArrayElementType();
|
|
} else {
|
|
Assertions.UNREACHABLE("Unexpected type " + arrayType.getClass());
|
|
}
|
|
if (elementType.isPrimitiveType()) {
|
|
if (doPrimitives && lhs.getType() == TypeAbstraction.TOP) {
|
|
lhs.setType(PrimitiveType.getPrimitive(elementType));
|
|
return CHANGED;
|
|
}
|
|
return NOT_CHANGED;
|
|
}
|
|
|
|
if (lhs.getType() != TypeAbstraction.TOP) {
|
|
TypeReference tType = null;
|
|
if (lhs.getType() instanceof PointType) {
|
|
tType = ((PointType) lhs.getType()).getType().getReference();
|
|
} else if (lhs.getType() instanceof ConeType) {
|
|
tType = ((ConeType) lhs.getType()).getType().getReference();
|
|
} else {
|
|
Assertions.UNREACHABLE("Unexpected type " + lhs.getType().getClass());
|
|
}
|
|
if (tType.equals(elementType)) {
|
|
return NOT_CHANGED;
|
|
} else {
|
|
IClass klass = cha.lookupClass(elementType);
|
|
assert klass != null;
|
|
lhs.setType(new ConeType(klass));
|
|
return CHANGED;
|
|
}
|
|
} else {
|
|
IClass klass = cha.lookupClass(elementType);
|
|
if (klass != null) {
|
|
lhs.setType(new ConeType(klass));
|
|
} else {
|
|
lhs.setType(TypeAbstraction.TOP);
|
|
}
|
|
|
|
return CHANGED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "getElementType " + load;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 9923 * load.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (o instanceof GetElementType) {
|
|
GetElementType other = (GetElementType) o;
|
|
return load.equals(other.load);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected class TypeOperatorFactory extends SSAInstruction.Visitor implements IVisitorWithAddresses, OperatorFactory<TypeVariable> {
|
|
|
|
protected AbstractOperator<TypeVariable> result = null;
|
|
|
|
@Override
|
|
public AbstractOperator<TypeVariable> get(SSAInstruction instruction) {
|
|
instruction.visit(this);
|
|
AbstractOperator<TypeVariable> temp = result;
|
|
result = null;
|
|
return temp;
|
|
}
|
|
|
|
@Override
|
|
public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
|
|
result = new GetElementType(instruction);
|
|
}
|
|
|
|
@Override
|
|
public void visitArrayLength(SSAArrayLengthInstruction instruction) {
|
|
if (!doPrimitives) {
|
|
result = null;
|
|
} else {
|
|
result = new DeclaredTypeOperator(language.getPrimitive(language.getConstantType(new Integer(1))));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
|
|
IClass jlClassKlass = cha.lookupClass(instruction.getType());
|
|
assert jlClassKlass != null;
|
|
result = new DeclaredTypeOperator(new ConeType(jlClassKlass));
|
|
}
|
|
|
|
@Override
|
|
public void visitGet(SSAGetInstruction instruction) {
|
|
TypeReference type = instruction.getDeclaredFieldType();
|
|
|
|
if (doPrimitives && type.isPrimitiveType()) {
|
|
PrimitiveType p = language.getPrimitive(type);
|
|
assert p != null : "no type for " + type;
|
|
result = new DeclaredTypeOperator(p);
|
|
} else {
|
|
IClass klass = cha.lookupClass(type);
|
|
if (klass == null) {
|
|
// get from a field of a type that cannot be loaded.
|
|
// be pessimistic
|
|
result = new DeclaredTypeOperator(BOTTOM);
|
|
} else {
|
|
result = new DeclaredTypeOperator(new ConeType(klass));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitInvoke(SSAInvokeInstruction instruction) {
|
|
TypeReference type = instruction.getDeclaredResultType();
|
|
if (type.isReferenceType()) {
|
|
IClass klass = cha.lookupClass(type);
|
|
if (klass == null) {
|
|
// a type that cannot be loaded.
|
|
// be pessimistic
|
|
result = new DeclaredTypeOperator(BOTTOM);
|
|
} else {
|
|
result = new DeclaredTypeOperator(new ConeType(klass));
|
|
}
|
|
} else if (doPrimitives && type.isPrimitiveType()) {
|
|
result = new DeclaredTypeOperator(language.getPrimitive(type));
|
|
} else {
|
|
result = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitNew(SSANewInstruction instruction) {
|
|
TypeReference type = instruction.getConcreteType();
|
|
IClass klass = cha.lookupClass(type);
|
|
if (klass == null) {
|
|
// a type that cannot be loaded.
|
|
// be pessimistic
|
|
result = new DeclaredTypeOperator(BOTTOM);
|
|
} else {
|
|
result = new DeclaredTypeOperator(new PointType(klass));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitCheckCast(SSACheckCastInstruction instruction) {
|
|
TypeAbstraction typeAbs = null;
|
|
for (TypeReference type : instruction.getDeclaredResultTypes()) {
|
|
IClass klass = cha.lookupClass(type);
|
|
if (klass == null) {
|
|
// a type that cannot be loaded.
|
|
// be pessimistic
|
|
typeAbs = BOTTOM;
|
|
} else {
|
|
TypeAbstraction x = null;
|
|
if (doPrimitives && type.isPrimitiveType()) {
|
|
x = language.getPrimitive(type);
|
|
} else if (type.isReferenceType()) {
|
|
x = new ConeType(klass);
|
|
}
|
|
if (x != null) {
|
|
if (typeAbs == null) {
|
|
typeAbs = x;
|
|
} else {
|
|
typeAbs = typeAbs.meet(x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = new DeclaredTypeOperator(typeAbs);
|
|
}
|
|
|
|
@Override
|
|
public void visitConversion(SSAConversionInstruction instruction) {
|
|
if (doPrimitives) {
|
|
result = new DeclaredTypeOperator(language.getPrimitive(instruction.getToType()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitComparison(SSAComparisonInstruction instruction) {
|
|
if (doPrimitives) {
|
|
result = new DeclaredTypeOperator(language.getPrimitive(language.getConstantType(new Integer(0))));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitBinaryOp(SSABinaryOpInstruction instruction) {
|
|
if (doPrimitives) {
|
|
result = primitivePropagateOp;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitUnaryOp(SSAUnaryOpInstruction instruction) {
|
|
if (doPrimitives) {
|
|
result = primitivePropagateOp;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitInstanceof(SSAInstanceofInstruction instruction) {
|
|
if (doPrimitives) {
|
|
result = new DeclaredTypeOperator(language.getPrimitive(language.getConstantType(Boolean.TRUE)));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
|
|
TypeAbstraction type = meetDeclaredExceptionTypes(instruction);
|
|
result = new DeclaredTypeOperator(type);
|
|
}
|
|
|
|
@Override
|
|
public void visitPhi(SSAPhiInstruction instruction) {
|
|
result = phiOp;
|
|
}
|
|
|
|
@Override
|
|
public void visitPi(SSAPiInstruction instruction) {
|
|
result = piOp;
|
|
}
|
|
|
|
private TypeAbstraction meetDeclaredExceptionTypes(SSAGetCaughtExceptionInstruction s) {
|
|
ExceptionHandlerBasicBlock bb = (ExceptionHandlerBasicBlock) ir.getControlFlowGraph().getNode(s.getBasicBlockNumber());
|
|
Iterator it = bb.getCaughtExceptionTypes();
|
|
TypeReference t = (TypeReference) it.next();
|
|
IClass klass = cha.lookupClass(t);
|
|
TypeAbstraction result = null;
|
|
if (klass == null) {
|
|
// a type that cannot be loaded.
|
|
// be pessimistic
|
|
result = BOTTOM;
|
|
} else {
|
|
result = new ConeType(klass);
|
|
}
|
|
while (it.hasNext()) {
|
|
t = (TypeReference) it.next();
|
|
IClass tClass = cha.lookupClass(t);
|
|
if (tClass == null) {
|
|
result = BOTTOM;
|
|
} else {
|
|
result = result.meet(new ConeType(tClass));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private DeclaredTypeOperator getPointerTypeOperator(TypeReference type) {
|
|
if (type.isPrimitiveType()) {
|
|
return new DeclaredTypeOperator(language.getPrimitive(type));
|
|
} else {
|
|
IClass klass = cha.lookupClass(type);
|
|
if (klass == null) {
|
|
// a type that cannot be loaded.
|
|
// be pessimistic
|
|
return new DeclaredTypeOperator(BOTTOM);
|
|
} else {
|
|
return new DeclaredTypeOperator(new ConeType(klass));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitAddressOf(SSAAddressOfInstruction instruction) {
|
|
TypeReference type = language.getPointerType(instruction.getType());
|
|
result = getPointerTypeOperator(type);
|
|
}
|
|
|
|
@Override
|
|
public void visitLoadIndirect(SSALoadIndirectInstruction instruction) {
|
|
result = getPointerTypeOperator(instruction.getLoadedType());
|
|
}
|
|
|
|
@Override
|
|
public void visitStoreIndirect(SSAStoreIndirectInstruction instruction) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
public class TypeVarFactory implements VariableFactory {
|
|
|
|
@Override
|
|
public IVariable makeVariable(int valueNumber) {
|
|
SymbolTable st = ir.getSymbolTable();
|
|
if (doPrimitives) {
|
|
if (st.isConstant(valueNumber)) {
|
|
if (st.isBooleanConstant(valueNumber)) {
|
|
return new TypeVariable(language.getPrimitive(language.getConstantType(Boolean.TRUE)));
|
|
}
|
|
}
|
|
}
|
|
|
|
return new TypeVariable(TypeAbstraction.TOP);
|
|
}
|
|
|
|
}
|
|
|
|
public IR getIR() {
|
|
return ir;
|
|
}
|
|
|
|
/**
|
|
* Return the type computed for a particular value number
|
|
*/
|
|
public TypeAbstraction getType(int valueNumber) {
|
|
if (valueNumber < 0) {
|
|
throw new IllegalArgumentException("bad value number " + valueNumber);
|
|
}
|
|
assert getVariable(valueNumber) != null : "null variable for value number " + valueNumber;
|
|
return getVariable(valueNumber).getType();
|
|
}
|
|
|
|
public TypeAbstraction getConstantType(int valueNumber) {
|
|
if (ir.getSymbolTable().isStringConstant(valueNumber)) {
|
|
return new PointType(cha.lookupClass(language.getStringType()));
|
|
} else {
|
|
return getConstantPrimitiveType(valueNumber);
|
|
}
|
|
}
|
|
|
|
public TypeAbstraction getConstantPrimitiveType(int valueNumber) {
|
|
SymbolTable st = ir.getSymbolTable();
|
|
if (!st.isConstant(valueNumber) || st.isNullConstant(valueNumber)) {
|
|
return TypeAbstraction.TOP;
|
|
} else {
|
|
return language.getPrimitive(language.getConstantType(st.getConstantValue(valueNumber)));
|
|
}
|
|
}
|
|
|
|
public boolean isUndefined(int valueNumber) {
|
|
// TODO: Julian, you seem to be using BOTTOM in the European style.
|
|
// Steve's code assumes American style (god forbid), so what you're getting
|
|
// here
|
|
// is not undefined, but java.lang.Object [NR/EY]
|
|
if (getVariable(valueNumber) == null) {
|
|
return true;
|
|
}
|
|
TypeAbstraction ta = getVariable(valueNumber).getType();
|
|
return ta == BOTTOM || ta.getType() == null;
|
|
}
|
|
|
|
/**
|
|
* Extract all results of the type inference analysis.
|
|
*
|
|
* @return an array, where the i'th variable holds the type abstraction of the i'th value number.
|
|
*/
|
|
public TypeAbstraction[] extractAllResults() {
|
|
int numberOfVars = ir.getSymbolTable().getMaxValueNumber() + 1;
|
|
TypeAbstraction[] ret = new TypeAbstraction[numberOfVars];
|
|
|
|
for (int i = 0; i < numberOfVars; ++i) {
|
|
TypeVariable var = getVariable(i);
|
|
ret[i] = var == null ? null : var.getType();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
@Override
|
|
protected TypeVariable[] makeStmtRHS(int size) {
|
|
return new TypeVariable[size];
|
|
}
|
|
}
|