212 lines
6.7 KiB
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;
|
|
}
|
|
}
|