WALA/com.ibm.wala.core/src/com/ibm/wala/ipa/summaries/MethodBypass.java

219 lines
6.3 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.summaries;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.strings.Atom;
/**
* "Non-standard" bypass rules to use during call graph construction.
*
* Normally, the method bypass rules replace the IMethod that is resolved by other means, via the getBypass() method. However, the
* bypass rules can be invoked even before resolving the target of a call, by checking the intercept rules.
*/
public class MethodBypass {
static final boolean DEBUG = false;
/**
* Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a
* <ul>
* <li>MethodReference
* <li>TypeReference
* <li>Atom (package name)
* </ul>
*/
private final Map methodSummaries;
/**
* Set of TypeReferences which are marked "allocatable"
*/
private final Set allocatable;
/**
* Governing class hierarchy.
*/
private final IClassHierarchy cha;
/**
* Mapping from MethodReference -&gt; SyntheticMethod
*/
final private HashMap<MethodReference, SummarizedMethod> syntheticMethods = HashMapFactory.make();
/**
* Set of method references that have been considered already.
*/
final private HashSet<MethodReference> considered = HashSetFactory.make();
public MethodBypass(Map methodSummaries, Set allocatable, IClassHierarchy cha) {
this.methodSummaries = methodSummaries;
this.allocatable = allocatable;
this.cha = cha;
}
/**
* Lookup bypass rules based on a method reference only.
*
* Method getBypass.
*
* @param m
* @return Object
*/
private SyntheticMethod getBypass(MethodReference m) {
if (DEBUG) {
System.err.println(("MethodBypass.getBypass? " + m));
}
SyntheticMethod result = findOrCreateSyntheticMethod(m);
if (result != null) {
return result;
}
// first lookup failed ... try resolving target via CHA and try again.
m = resolveTarget(m);
return findOrCreateSyntheticMethod(m);
}
/**
* @param m a method reference
* @return a SyntheticMethod corresponding to m; or null if none is available.
*/
private SyntheticMethod findOrCreateSyntheticMethod(MethodReference m) {
if (considered.contains(m)) {
return syntheticMethods.get(m);
} else {
considered.add(m);
MethodSummary summ = findSummary(m);
if (summ != null) {
TypeReference T = m.getDeclaringClass();
IClass c = cha.lookupClass(T);
assert c != null : "null class for " + T;
SummarizedMethod n = new SummarizedMethod(m, summ, c);
syntheticMethods.put(m, n);
return n;
}
return null;
}
}
private MethodSummary findSummary(MemberReference m) {
MethodSummary result = (MethodSummary) methodSummaries.get(m);
if (result != null) {
if (DEBUG) {
System.err.println(("findSummary succeeded: " + m));
}
return result;
}
// try the class instead.
TypeReference t = m.getDeclaringClass();
result = (MethodSummary) methodSummaries.get(t);
if (result != null) {
if (DEBUG) {
System.err.println(("findSummary succeeded: " + t));
}
return result;
}
if (t.isArrayType())
return null;
// finally try the package.
Atom p = extractPackage(t);
result = (MethodSummary) methodSummaries.get(p);
if (result != null) {
if (DEBUG) {
System.err.println(("findSummary succeeded: " + p));
}
return result;
} else {
if (DEBUG) {
System.err.println(("findSummary failed: " + m));
}
return result;
}
}
/**
* Method getBypass. check to see if a call to the receiver 'target' should be redirected to a different receiver.
*
* @param target
* @return Object
* @throws IllegalArgumentException if target is null
*/
public SyntheticMethod getBypass(IMethod target) {
if (target == null) {
throw new IllegalArgumentException("target is null");
}
return getBypass(target.getReference());
}
/**
* Method extractPackage.
*
* @param type
* @return Atom that represents the package name, or null if this is the unnamed package.
*/
private static Atom extractPackage(TypeReference type) {
String s = type.getName().toString();
int index = s.lastIndexOf('/');
if (index == -1) {
return null;
} else {
s = s.substring(0, index);
return Atom.findOrCreateAsciiAtom(s);
}
}
protected IClassHierarchy getClassHierarchy() {
return cha;
}
protected MethodReference resolveTarget(MethodReference target) {
IMethod m = getClassHierarchy().resolveMethod(target);
if (m != null) {
if (DEBUG) {
System.err.println(("resolveTarget: resolved to " + m));
}
target = m.getReference();
}
return target;
}
/**
* Are we allowed to allocate (for analysis purposes) an instance of a given type? By default, the answer is yes iff T is not
* abstract. However, subclasses and summaries can override this to allow "special" abstract classes to be allocatable as well.
*
* @throws IllegalArgumentException if klass is null
*
*/
public boolean isAllocatable(IClass klass) {
if (klass == null) {
throw new IllegalArgumentException("klass is null");
}
if (!klass.isAbstract() && !klass.isInterface()) {
return true;
} else {
return allocatable.contains(klass.getReference());
}
}
}