/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Stefan Hepp (stefan@stefant.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common.code;
import com.jopdesign.common.AppInfo;
import com.jopdesign.common.MethodCode;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.CallGraph.CallgraphBuilder;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Provide config options and callback methods to setup and build the callgraph.
*
* @author Stefan Hepp (stefan@stefant.org)
*/
public class DefaultCallgraphBuilder implements CallgraphBuilder {
private final int callstringLength;
private boolean skipNatives = false;
private boolean useCallgraph = true;
public DefaultCallgraphBuilder() {
this.callstringLength = AppInfo.getSingleton().getCallstringLength();
}
public DefaultCallgraphBuilder(int callstringLength) {
this.callstringLength = callstringLength;
}
public DefaultCallgraphBuilder(int callstringLength, boolean useCallgraph) {
this.callstringLength = callstringLength;
this.useCallgraph = useCallgraph;
}
public int getCallstringLength() {
return callstringLength;
}
public boolean doSkipNatives() {
return skipNatives;
}
public void setSkipNatives(boolean skipNatives) {
this.skipNatives = skipNatives;
}
public boolean doUseCallgraph() {
return useCallgraph;
}
public void setUseCallgraph(boolean useCallgraph) {
this.useCallgraph = useCallgraph;
}
@Override
public Set<ExecutionContext> getInvokedMethods(ExecutionContext context) {
CallString callstring = context.getCallString();
MethodInfo method = context.getMethodInfo();
if (!method.hasCode()) {
//noinspection unchecked
return (Set<ExecutionContext>) Collections.EMPTY_SET;
}
// TODO use only callstring length 1, split nodes only if required using DFA/.. later on?
Set<ExecutionContext> newContexts = new LinkedHashSet<ExecutionContext>();
invokeLoop:
for(InvokeSite invokeSite : findInvokeSites(method.getCode())) {
Set<MethodInfo> methods = getInvokedMethods(context, invokeSite);
if (skipNatives) {
for (MethodInfo impl : methods) {
// we do not want to have native methods in the callgraph
// we do not return any method for an invokesite even if only some of them are native,
// to indicate that the candidates are not completely represented!
if (impl.isNative()) {
continue invokeLoop;
}
}
}
for(MethodInfo impl : methods) {
//System.out.println("Implemented Methods: "+impl+" from "+iNode.getBasicBlock().getMethodInfo().methodId+" in context "+callstring.toStringVerbose());
CallString newCallString = callstring.push(invokeSite, callstringLength);
newContexts.add(new ExecutionContext(impl, newCallString));
}
}
return newContexts;
}
/**
* @param code the method to process
* @return a set of invokeSites in this method which should be included in the constructed callgraph.
*/
protected Set<InvokeSite> findInvokeSites(MethodCode code) {
return code.getInvokeSites();
}
/**
* Get a list of a all invoke candidates for an invokesite
* @param context the context, containing the callstring up to the invoke site
* @param invokeSite the invokesite
* @return a set of possible implementations
*/
protected Set<MethodInfo> getInvokedMethods(ExecutionContext context, InvokeSite invokeSite) {
if (useCallgraph) {
// This uses either the existing default callgraph to construct a new one (which has
// the advantage that the new callgraph is derived from an existing one), or looks up
// in the type graph if no default callgraph exists
return AppInfo.getSingleton().findImplementations(invokeSite, context.getCallString());
} else {
return AppInfo.getSingleton().findImplementations(invokeSite.getInvokeeRef());
}
}
}