WALA/com.ibm.wala.core/src/com/ibm/wala/ipa/summaries/BypassMethodTargetSelector....

284 lines
9.2 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.Map;
import java.util.Set;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.impl.ClassHierarchyMethodTargetSelector;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
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.
*
* @author sfink
*/
public class BypassMethodTargetSelector implements MethodTargetSelector {
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<MethodReference, MethodSummary> methodSummaries;
/**
* Set of Atoms representing package names whose methods should be treated as no-ops
*/
private final Set<Atom> ignoredPackages;
/**
* Governing class hierarchy.
*/
protected final IClassHierarchy cha;
/**
* target selector to use for non-bypassed calls
*/
protected final MethodTargetSelector parent;
/**
* for checking method target resolution via CHA
*/
private final ClassHierarchyMethodTargetSelector chaMethodTargetSelector;
/**
* Mapping from MethodReference -> SyntheticMethod We may call syntheticMethod.put(m,null) .. in which case we use containsKey()
* to check for having already considered m.
*/
final private HashMap<MethodReference, SummarizedMethod> syntheticMethods = HashMapFactory.make();
/**
* @param parent
* @param methodSummaries
* @param ignoredPackages
* @param cha
*/
public BypassMethodTargetSelector(MethodTargetSelector parent, Map<MethodReference, MethodSummary> methodSummaries,
Set<Atom> ignoredPackages, IClassHierarchy cha) {
this.methodSummaries = methodSummaries;
this.ignoredPackages = ignoredPackages;
this.parent = parent;
this.cha = cha;
this.chaMethodTargetSelector = new ClassHierarchyMethodTargetSelector(cha);
}
/**
* Check to see if a particular call site should be bypassed, before checking normal resolution of the receiver.
*
* @throws IllegalArgumentException if site is null
*/
@Override
public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass dispatchType) {
if (site == null) {
throw new IllegalArgumentException("site is null");
}
// first, see if we'd like to bypass the CHA-based target for the site
MethodReference ref = site.getDeclaredTarget();
IMethod chaTarget = chaMethodTargetSelector.getCalleeTarget(caller, site, dispatchType);
IMethod target = (chaTarget == null) ? findOrCreateSyntheticMethod(ref, site.isStatic()) : findOrCreateSyntheticMethod(chaTarget,
site.isStatic());
if (DEBUG) {
System.err.println("target is initially " + target);
}
if (target != null) {
return target;
} else {
// didn't bypass the CHA target; check if we should bypass the parent target
if (canIgnore(site.getDeclaredTarget())) {
// we want to generate a NoOpSummary for this method.
return findOrCreateSyntheticMethod(site.getDeclaredTarget(), site.isStatic());
}
// not using if (instanceof ClassHierarchyMethodTargetSelector) because
// we want to make sure that getCalleeTarget() is still called if
// parent is a subclass of ClassHierarchyMethodTargetSelector
if (parent.getClass() == ClassHierarchyMethodTargetSelector.class) {
// already checked this case and decided not to bypass
return chaTarget;
}
target = parent.getCalleeTarget(caller, site, dispatchType);
if (DEBUG) {
System.err.println("target becomes " + target);
}
if (target != null) {
IMethod bypassTarget = findOrCreateSyntheticMethod(target, site.isStatic());
if (DEBUG)
System.err.println("bypassTarget is " + target);
return (bypassTarget == null) ? target : bypassTarget;
} else
return target;
}
}
/**
* @param m a method reference
* @return a SyntheticMethod corresponding to m; or null if none is available.
*/
protected SyntheticMethod findOrCreateSyntheticMethod(MethodReference m, boolean isStatic) {
if (syntheticMethods.containsKey(m)) {
return syntheticMethods.get(m);
} else {
MethodSummary summ = null;
if (canIgnore(m)) {
TypeReference T = m.getDeclaringClass();
IClass C = cha.lookupClass(T);
if (C == null) {
// did not load class; don't try to create a synthetic method
syntheticMethods.put(m, null);
return null;
}
summ = generateNoOp(m, isStatic);
} else {
summ = findSummary(m);
}
if (summ != null) {
TypeReference T = m.getDeclaringClass();
IClass C = cha.lookupClass(T);
if (C == null) {
syntheticMethods.put(m, null);
return null;
}
SummarizedMethod n = new SummarizedMethod(m, summ, C);
syntheticMethods.put(m, n);
return n;
} else {
syntheticMethods.put(m, null);
return null;
}
}
}
/**
* @param m a method reference
* @return a SyntheticMethod corresponding to m; or null if none is available.
*/
protected SyntheticMethod findOrCreateSyntheticMethod(IMethod m, boolean isStatic) {
MethodReference ref = m.getReference();
if (syntheticMethods.containsKey(ref)) {
return syntheticMethods.get(ref);
} else {
MethodSummary summ = null;
if (canIgnore(ref)) {
summ = generateNoOp(ref, isStatic);
} else {
summ = findSummary(ref);
}
if (summ != null) {
SummarizedMethod n = new SummarizedMethod(ref, summ, m.getDeclaringClass());
syntheticMethods.put(ref, n);
return n;
} else {
syntheticMethods.put(ref, null);
return null;
}
}
}
/**
* Generate a {@link MethodSummary} which is the "standard" representation of a method
* that does nothing.
*/
public static MethodSummary generateStandardNoOp(Language l, MethodReference m, boolean isStatic) {
return new NoOpSummary(l, m, isStatic);
}
/**
* Generate a {@link MethodSummary} which is the "standard" representation of a method
* that does nothing. Subclasses may override this method to implement alternative semantics
* concerning what "do nothing" means.
*/
public MethodSummary generateNoOp(MethodReference m, boolean isStatic) {
Language l = cha.resolveMethod(m).getDeclaringClass().getClassLoader().getLanguage();
return new NoOpSummary(l, m, isStatic);
}
private static class NoOpSummary extends MethodSummary {
private final Language l;
public NoOpSummary(Language l, MethodReference method, boolean isStatic) {
super(method);
setStatic(isStatic);
this.l = l;
}
/*
* @see com.ibm.wala.ipa.summaries.MethodSummary#getStatements()
*/
@Override
public SSAInstruction[] getStatements() {
if (getReturnType().equals(TypeReference.Void)) {
return NO_STATEMENTS;
} else {
int nullValue = getNumberOfParameters() + 1;
SSAInstruction[] result = new SSAInstruction[1];
SSAInstructionFactory insts = l.instructionFactory();
result[0] = insts.ReturnInstruction(0, nullValue, getReturnType().isPrimitiveType());
return result;
}
}
}
/**
* @param m
* @return true iff we can treat m as a no-op method
*/
protected boolean canIgnore(MemberReference m) {
TypeReference T = m.getDeclaringClass();
TypeName n = T.getName();
Atom p = n.getPackage();
return (ignoredPackages.contains(p));
}
private MethodSummary findSummary(MemberReference m) {
return methodSummaries.get(m);
}
protected IClassHierarchy getClassHierarchy() {
return cha;
}
}