WALA/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/Entrypoint.java

212 lines
6.7 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;
import java.util.Arrays;
import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.PrimitiveType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.debug.Assertions;
/**
* A representation of an entrypoint in the call graph.
*/
public abstract class Entrypoint implements BytecodeConstants {
/**
* The method to be called
*/
protected final IMethod method;
/**
* @param method the method to be called for this entrypoint
*/
protected Entrypoint(IMethod method) {
if (method == null) {
throw new IllegalArgumentException("method is null");
}
this.method = method;
assert method.getDeclaringClass() != null : "null declaring class";
}
protected Entrypoint(MethodReference method, IClassHierarchy cha) {
if (cha == null) {
throw new IllegalArgumentException("cha is null");
}
IMethod m = cha.resolveMethod(method);
if (m == null) {
Assertions.UNREACHABLE("could not resolve " + method);
}
this.method = m;
}
/**
* Create a call site reference representing a call to this entrypoint
*
* @param programCounter the bytecode index of the synthesize call
* @return the call site reference, or null if failed to find entrypoint
*/
public CallSiteReference makeSite(int programCounter) {
if (method.getSelector().equals(MethodReference.clinitSelector)) {
assert method.isStatic();
return CallSiteReference.make(programCounter, method.getReference(), IInvokeInstruction.Dispatch.STATIC);
} else if (method.getSelector().equals(MethodReference.initSelector)) {
assert !method.isStatic();
return CallSiteReference.make(programCounter, method.getReference(), IInvokeInstruction.Dispatch.SPECIAL);
} else {
if (method.getDeclaringClass().isInterface()) {
return CallSiteReference.make(programCounter, method.getReference(), IInvokeInstruction.Dispatch.INTERFACE);
} else {
if (method.isStatic()) {
return CallSiteReference.make(programCounter, method.getReference(), IInvokeInstruction.Dispatch.STATIC);
} else {
return CallSiteReference.make(programCounter, method.getReference(), IInvokeInstruction.Dispatch.VIRTUAL);
}
}
}
}
/**
* Add allocation statements to the fake root method for each possible value of parameter i. If necessary, add a phi to combine
* the values.
*
* @return value number holding the parameter to the call; -1 if there was some error
*/
protected int makeArgument(AbstractRootMethod m, int i) {
TypeReference[] p = getParameterTypes(i);
if (p.length == 0) {
return -1;
} else if (p.length == 1) {
if (p[0].isPrimitiveType()) {
return m.addLocal();
} else {
SSANewInstruction n = m.addAllocation(p[0]);
return (n == null) ? -1 : n.getDef();
}
} else {
int[] values = new int[p.length];
int countErrors = 0;
for (int j = 0; j < p.length; j++) {
SSANewInstruction n = m.addAllocation(p[j]);
int value = (n == null) ? -1 : n.getDef();
if (value == -1) {
countErrors++;
} else {
values[j - countErrors] = value;
}
}
if (countErrors > 0) {
int[] oldValues = values;
values = new int[oldValues.length - countErrors];
System.arraycopy(oldValues, 0, values, 0, values.length);
}
TypeAbstraction a;
if (p[0].isPrimitiveType()) {
a = PrimitiveType.getPrimitive(p[0]);
for (i = 1; i < p.length; i++) {
a = a.meet(PrimitiveType.getPrimitive(p[i]));
}
} else {
IClassHierarchy cha = m.getClassHierarchy();
IClass p0 = cha.lookupClass(p[0]);
a = new ConeType(p0);
for (i = 1; i < p.length; i++) {
IClass pi = cha.lookupClass(p[i]);
a = a.meet(new ConeType(pi));
}
}
return m.addPhi(values);
}
}
@Override
public boolean equals(Object obj) {
// assume these are managed canonically
return this == obj;
}
/**
* Add a call to this entrypoint from the fake root method
*
* @param m the Fake Root Method
* @return the call instruction added, or null if the operation fails
*/
public SSAAbstractInvokeInstruction addCall(AbstractRootMethod m) {
int paramValues[];
CallSiteReference site = makeSite(0);
if (site == null) {
return null;
}
paramValues = new int[getNumberOfParameters()];
for (int j = 0; j < paramValues.length; j++) {
paramValues[j] = makeArgument(m, j);
if (paramValues[j] == -1) {
// there was a problem
return null;
}
}
return m.addInvocation(paramValues, site);
}
/**
* @return the method this call invokes
*/
public IMethod getMethod() {
return method;
}
/**
* @return types to allocate for parameter i; for non-static methods, parameter 0 is "this"
*/
public abstract TypeReference[] getParameterTypes(int i);
/**
* @return number of parameters to this call, including "this" for non-statics
*/
public abstract int getNumberOfParameters();
@Override
public String toString() {
StringBuffer result = new StringBuffer(method.toString());
result.append("(");
for (int i = 0; i < getNumberOfParameters() - 1; i++) {
result.append(Arrays.toString(getParameterTypes(i)));
result.append(",");
}
if (getNumberOfParameters() > 0) {
result.append(Arrays.toString(getParameterTypes(getNumberOfParameters() - 1)));
}
result.append(")");
return result.toString();
}
@Override
public int hashCode() {
return method.hashCode() * 1009;
}
}