422 lines
15 KiB
Java
422 lines
15 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.ipa.callgraph.impl;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
|
|
import com.ibm.wala.cfg.InducedCFG;
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
import com.ibm.wala.classLoader.IClass;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.classLoader.NewSiteReference;
|
|
import com.ibm.wala.classLoader.SyntheticMethod;
|
|
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
|
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
import com.ibm.wala.ipa.callgraph.Context;
|
|
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
import com.ibm.wala.ipa.summaries.SyntheticIR;
|
|
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
|
import com.ibm.wala.ssa.ConstantValue;
|
|
import com.ibm.wala.ssa.IR;
|
|
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAInstructionFactory;
|
|
import com.ibm.wala.ssa.SSAInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSANewInstruction;
|
|
import com.ibm.wala.ssa.SSAOptions;
|
|
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
import com.ibm.wala.ssa.SSAReturnInstruction;
|
|
import com.ibm.wala.types.FieldReference;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.collections.EmptyIterator;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.warnings.Warning;
|
|
import com.ibm.wala.util.warnings.Warnings;
|
|
|
|
/**
|
|
* A synthetic method from the {@link FakeRootClass}
|
|
*/
|
|
public abstract class AbstractRootMethod extends SyntheticMethod {
|
|
|
|
final protected ArrayList<SSAInstruction> statements = new ArrayList<SSAInstruction>();
|
|
|
|
private Map<ConstantValue, Integer> constant2ValueNumber = HashMapFactory.make();
|
|
|
|
/**
|
|
* The number of the next local value number available for the fake root method. Note that we reserve value number 1 to represent
|
|
* the value "any exception caught by the root method"
|
|
*/
|
|
protected int nextLocal = 2;
|
|
|
|
protected final IClassHierarchy cha;
|
|
|
|
private final AnalysisOptions options;
|
|
|
|
protected final AnalysisCache cache;
|
|
|
|
protected final SSAInstructionFactory insts;
|
|
|
|
public AbstractRootMethod(MethodReference method, IClass declaringClass, final IClassHierarchy cha, AnalysisOptions options,
|
|
AnalysisCache cache) {
|
|
super(method, declaringClass, true, false);
|
|
this.cha = cha;
|
|
this.options = options;
|
|
this.cache = cache;
|
|
this.insts = declaringClass.getClassLoader().getInstructionFactory();
|
|
if (cache == null) {
|
|
throw new IllegalArgumentException("null cache");
|
|
}
|
|
// I'd like to enforce that declaringClass is a FakeRootClass ... but CASt would currently break.
|
|
// so checking dynamically instead.
|
|
if (declaringClass instanceof FakeRootClass) {
|
|
((FakeRootClass) declaringClass).addMethod(this);
|
|
}
|
|
}
|
|
|
|
public AbstractRootMethod(MethodReference method, final IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
|
|
this(method, new FakeRootClass(cha), cha, options, cache);
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.classLoader.IMethod#getStatements(com.ibm.wala.util.warnings.WarningSet)
|
|
*/
|
|
@Override
|
|
public SSAInstruction[] getStatements(SSAOptions options) {
|
|
SSAInstruction[] result = new SSAInstruction[statements.size()];
|
|
int i = 0;
|
|
for (Iterator<SSAInstruction> it = statements.iterator(); it.hasNext();) {
|
|
result[i++] = it.next();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public IR makeIR(Context context, SSAOptions options) {
|
|
SSAInstruction instrs[] = getStatements(options);
|
|
Map<Integer, ConstantValue> constants = null;
|
|
if (!constant2ValueNumber.isEmpty()) {
|
|
constants = HashMapFactory.make(constant2ValueNumber.size());
|
|
for (ConstantValue c : constant2ValueNumber.keySet()) {
|
|
int vn = constant2ValueNumber.get(c);
|
|
constants.put(vn, c);
|
|
}
|
|
}
|
|
InducedCFG cfg = makeControlFlowGraph(instrs);
|
|
return new SyntheticIR(this, Everywhere.EVERYWHERE, cfg, instrs, options, constants);
|
|
}
|
|
|
|
public int addLocal() {
|
|
return nextLocal++;
|
|
}
|
|
|
|
/**
|
|
* @return the invoke instructions added by this operation
|
|
* @throws IllegalArgumentException if site is null
|
|
*/
|
|
public SSAInvokeInstruction addInvocation(int[] params, CallSiteReference site) {
|
|
if (site == null) {
|
|
throw new IllegalArgumentException("site is null");
|
|
}
|
|
CallSiteReference newSite = CallSiteReference.make(statements.size(), site.getDeclaredTarget(), site.getInvocationCode());
|
|
SSAInvokeInstruction s = null;
|
|
if (newSite.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
|
|
s = insts.InvokeInstruction(statements.size(), params, nextLocal++, newSite);
|
|
} else {
|
|
s = insts.InvokeInstruction(statements.size(), nextLocal++, params, nextLocal++, newSite);
|
|
}
|
|
statements.add(s);
|
|
cache.invalidate(this, Everywhere.EVERYWHERE);
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Add a return statement
|
|
*/
|
|
public SSAReturnInstruction addReturn(int vn, boolean isPrimitive) {
|
|
SSAReturnInstruction s = insts.ReturnInstruction(statements.size(), vn, isPrimitive);
|
|
statements.add(s);
|
|
cache.invalidate(this, Everywhere.EVERYWHERE);
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Add a New statement of the given type
|
|
*
|
|
* Side effect: adds call to default constructor of given type if one exists.
|
|
*
|
|
* @return instruction added, or null
|
|
* @throws IllegalArgumentException if T is null
|
|
*/
|
|
public SSANewInstruction addAllocation(TypeReference T) {
|
|
return addAllocation(T, true);
|
|
}
|
|
|
|
/**
|
|
* Add a New statement of the given array type and length
|
|
*/
|
|
public SSANewInstruction add1DArrayAllocation(TypeReference T, int length) {
|
|
int instance = nextLocal++;
|
|
NewSiteReference ref = NewSiteReference.make(statements.size(), T);
|
|
assert T.isArrayType();
|
|
assert T.getDimensionality() == 1;
|
|
int[] sizes = new int[1];
|
|
Arrays.fill(sizes, getValueNumberForIntConstant(length));
|
|
SSANewInstruction result = insts.NewInstruction(statements.size(), instance, ref, sizes);
|
|
statements.add(result);
|
|
cache.invalidate(this, Everywhere.EVERYWHERE);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Add a New statement of the given type
|
|
*/
|
|
public SSANewInstruction addAllocationWithoutCtor(TypeReference T) {
|
|
return addAllocation(T, false);
|
|
}
|
|
|
|
/**
|
|
* Add a New statement of the given type
|
|
*
|
|
* @return instruction added, or null
|
|
* @throws IllegalArgumentException if T is null
|
|
*/
|
|
private SSANewInstruction addAllocation(TypeReference T, boolean invokeCtor) {
|
|
if (T == null) {
|
|
throw new IllegalArgumentException("T is null");
|
|
}
|
|
int instance = nextLocal++;
|
|
SSANewInstruction result = null;
|
|
|
|
if (T.isReferenceType()) {
|
|
NewSiteReference ref = NewSiteReference.make(statements.size(), T);
|
|
if (T.isArrayType()) {
|
|
int[] sizes = new int[T.getDimensionality()];
|
|
Arrays.fill(sizes, getValueNumberForIntConstant(1));
|
|
result = insts.NewInstruction(statements.size(), instance, ref, sizes);
|
|
} else {
|
|
result = insts.NewInstruction(statements.size(), instance, ref);
|
|
}
|
|
statements.add(result);
|
|
|
|
IClass klass = cha.lookupClass(T);
|
|
if (klass == null) {
|
|
Warnings.add(AllocationFailure.create(T));
|
|
return null;
|
|
}
|
|
|
|
if (klass.isArrayClass()) {
|
|
int arrayRef = result.getDef();
|
|
TypeReference e = klass.getReference().getArrayElementType();
|
|
while (e != null && !e.isPrimitiveType()) {
|
|
// allocate an instance for the array contents
|
|
NewSiteReference n = NewSiteReference.make(statements.size(), e);
|
|
int alloc = nextLocal++;
|
|
SSANewInstruction ni = null;
|
|
if (e.isArrayType()) {
|
|
int[] sizes = new int[T.getDimensionality()];
|
|
Arrays.fill(sizes, getValueNumberForIntConstant(1));
|
|
ni = insts.NewInstruction(statements.size(), alloc, n, sizes);
|
|
} else {
|
|
ni = insts.NewInstruction(statements.size(), alloc, n);
|
|
}
|
|
statements.add(ni);
|
|
|
|
// emit an astore
|
|
SSAArrayStoreInstruction store = insts.ArrayStoreInstruction(statements.size(), arrayRef, getValueNumberForIntConstant(0), alloc, e);
|
|
statements.add(store);
|
|
|
|
e = e.isArrayType() ? e.getArrayElementType() : null;
|
|
arrayRef = alloc;
|
|
}
|
|
}
|
|
if (invokeCtor) {
|
|
IMethod ctor = cha.resolveMethod(klass, MethodReference.initSelector);
|
|
if (ctor != null) {
|
|
addInvocation(new int[] { instance }, CallSiteReference.make(statements.size(), ctor.getReference(),
|
|
IInvokeInstruction.Dispatch.SPECIAL));
|
|
}
|
|
}
|
|
}
|
|
cache.invalidate(this, Everywhere.EVERYWHERE);
|
|
return result;
|
|
}
|
|
|
|
protected int getValueNumberForIntConstant(int c) {
|
|
ConstantValue v = new ConstantValue(c);
|
|
Integer result = constant2ValueNumber.get(v);
|
|
if (result == null) {
|
|
result = nextLocal++;
|
|
constant2ValueNumber.put(v, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected int getValueNumberForByteConstant(byte c) {
|
|
// treat it like an int constant for now.
|
|
ConstantValue v = new ConstantValue(c);
|
|
Integer result = constant2ValueNumber.get(v);
|
|
if (result == null) {
|
|
result = nextLocal++;
|
|
constant2ValueNumber.put(v, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected int getValueNumberForCharConstant(char c) {
|
|
// treat it like an int constant for now.
|
|
ConstantValue v = new ConstantValue(c);
|
|
Integer result = constant2ValueNumber.get(v);
|
|
if (result == null) {
|
|
result = nextLocal++;
|
|
constant2ValueNumber.put(v, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* A warning for when we fail to allocate a type in the fake root method
|
|
*/
|
|
private static class AllocationFailure extends Warning {
|
|
|
|
final TypeReference t;
|
|
|
|
AllocationFailure(TypeReference t) {
|
|
super(Warning.SEVERE);
|
|
this.t = t;
|
|
}
|
|
|
|
@Override
|
|
public String getMsg() {
|
|
return getClass().toString() + " : " + t;
|
|
}
|
|
|
|
public static AllocationFailure create(TypeReference t) {
|
|
return new AllocationFailure(t);
|
|
}
|
|
}
|
|
|
|
public int addPhi(int[] values) {
|
|
int result = nextLocal++;
|
|
SSAPhiInstruction phi = insts.PhiInstruction(statements.size(), result, values);
|
|
statements.add(phi);
|
|
return result;
|
|
}
|
|
|
|
public int addGetInstance(FieldReference ref, int object) {
|
|
int result = nextLocal++;
|
|
statements.add(insts.GetInstruction(statements.size(), result, object, ref));
|
|
return result;
|
|
}
|
|
|
|
public int addGetStatic(FieldReference ref) {
|
|
int result = nextLocal++;
|
|
statements.add(insts.GetInstruction(statements.size(), result, ref));
|
|
return result;
|
|
}
|
|
|
|
public int addCheckcast(TypeReference[] types, int rv) {
|
|
int lv = nextLocal++;
|
|
|
|
statements.add(insts.CheckCastInstruction(statements.size(), lv, rv, types));
|
|
return lv;
|
|
}
|
|
|
|
/** BEGIN Custom change: advanced synthetic instructions */
|
|
public void addSetInstance(final FieldReference ref, final int baseObject, final int value) {
|
|
statements.add(insts.PutInstruction(statements.size(), baseObject, value, ref));
|
|
}
|
|
|
|
public void addSetStatic(final FieldReference ref, final int value) {
|
|
statements.add(insts.PutInstruction(statements.size(), value, ref));
|
|
}
|
|
|
|
public void addSetArrayField(final TypeReference elementType, final int baseObject, final int indexValue, final int value) {
|
|
statements.add(insts.ArrayStoreInstruction(statements.size(), baseObject, indexValue, value, elementType));
|
|
}
|
|
|
|
public int addGetArrayField(final TypeReference elementType, final int baseObject, final int indexValue) {
|
|
int result = nextLocal++;
|
|
statements.add(insts.ArrayLoadInstruction(statements.size(), result, baseObject, indexValue, elementType));
|
|
return result;
|
|
}
|
|
/** END Custom change: advanced synthetic instructions */
|
|
public RTAContextInterpreter getInterpreter() {
|
|
return new RTAContextInterpreter() {
|
|
|
|
public Iterator<NewSiteReference> iterateNewSites(CGNode node) {
|
|
ArrayList<NewSiteReference> result = new ArrayList<NewSiteReference>();
|
|
SSAInstruction[] statements = getStatements(options.getSSAOptions());
|
|
for (int i = 0; i < statements.length; i++) {
|
|
if (statements[i] instanceof SSANewInstruction) {
|
|
SSANewInstruction s = (SSANewInstruction) statements[i];
|
|
result.add(s.getNewSite());
|
|
}
|
|
}
|
|
return result.iterator();
|
|
}
|
|
|
|
public Iterator<SSAInstruction> getInvokeStatements() {
|
|
ArrayList<SSAInstruction> result = new ArrayList<SSAInstruction>();
|
|
SSAInstruction[] statements = getStatements(options.getSSAOptions());
|
|
for (int i = 0; i < statements.length; i++) {
|
|
if (statements[i] instanceof SSAInvokeInstruction) {
|
|
result.add(statements[i]);
|
|
}
|
|
}
|
|
return result.iterator();
|
|
}
|
|
|
|
public Iterator<CallSiteReference> iterateCallSites(CGNode node) {
|
|
final Iterator<SSAInstruction> I = getInvokeStatements();
|
|
return new Iterator<CallSiteReference>() {
|
|
public boolean hasNext() {
|
|
return I.hasNext();
|
|
}
|
|
|
|
public CallSiteReference next() {
|
|
SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
|
|
return s.getCallSite();
|
|
}
|
|
|
|
public void remove() {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
};
|
|
}
|
|
|
|
public boolean understands(CGNode node) {
|
|
return node.getMethod().getDeclaringClass().getReference().equals(FakeRootClass.FAKE_ROOT_CLASS);
|
|
}
|
|
|
|
public boolean recordFactoryType(CGNode node, IClass klass) {
|
|
// not a factory type
|
|
return false;
|
|
}
|
|
|
|
public Iterator<FieldReference> iterateFieldsRead(CGNode node) {
|
|
return EmptyIterator.instance();
|
|
}
|
|
|
|
public Iterator<FieldReference> iterateFieldsWritten(CGNode node) {
|
|
return EmptyIterator.instance();
|
|
}
|
|
};
|
|
}
|
|
}
|