blob: 48442b5f64150aa205ebef494fe0d4713c417f2e [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.Collection;
import java.util.Set;
/**
* Call graph, which records {callee->callers} and {caller->callees} pairs.
*/
public class CallGraph {
/**
* Visitor used to build call graph.
*/
private class BuildCallGraphVisitor extends JVisitor {
// TODO(leafwang): This call graph does not take into account overloads nor calls that happen
// in JSNI methods.
private JMethod currentMethod;
@Override
public void endVisit(JMethod x, Context ctx) {
assert (currentMethod == x);
currentMethod = null;
}
@Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod calleeMethod = x.getTarget();
assert (currentMethod != null);
calleeCallersPairs.put(calleeMethod, currentMethod);
callerCalleesPairs.put(currentMethod, calleeMethod);
}
@Override
public boolean visit(JMethod x, Context ctx) {
assert (currentMethod == null);
currentMethod = x;
return true;
}
}
private LinkedHashMultimap<JMethod, JMethod> calleeCallersPairs = LinkedHashMultimap.create();
private LinkedHashMultimap<JMethod, JMethod> callerCalleesPairs = LinkedHashMultimap.create();
/**
* Add a caller method and its callee methods to the call graph.
*/
public void addCallerMethod(JMethod callerMethod, Collection<JMethod> calleeMethods) {
callerCalleesPairs.putAll(callerMethod, calleeMethods);
for (JMethod calleeMethod : calleeMethods) {
calleeCallersPairs.put(calleeMethod, callerMethod);
}
}
/**
* Build the call graph of a JProgram.
*/
public void buildCallGraph(JProgram program) {
resetCallGraph();
BuildCallGraphVisitor buildCallGraphVisitor = new BuildCallGraphVisitor();
buildCallGraphVisitor.accept(program);
}
/**
* Return all the callee methods in the call graph.
*/
public Set<JMethod> getAllCallees() {
return calleeCallersPairs.keySet();
}
/**
* Return all the callees of a set of caller methods.
*/
public Set<JMethod> getCallees(Collection<JMethod> callerMethods) {
assert (callerMethods != null);
Set<JMethod> calleeMethods = Sets.newLinkedHashSet();
for (JMethod callerMethod : callerMethods) {
calleeMethods.addAll(callerCalleesPairs.get(callerMethod));
}
return calleeMethods;
}
/**
* Return all the callers of a set of callee methods.
*/
public Set<JMethod> getCallers(Collection<JMethod> calleeMethods) {
assert (calleeMethods != null);
Set<JMethod> callerMethods = Sets.newLinkedHashSet();
for (JMethod calleeMethod : calleeMethods) {
callerMethods.addAll(calleeCallersPairs.get(calleeMethod));
}
return callerMethods;
}
/**
* Remove a callee method and all its caller methods in both
* {@code calleeCallersPairs} and {@code callerCalleesPairs}.
* Return its caller methods.
*/
public Set<JMethod> removeCalleeMethod(JMethod calleeMethod) {
Set<JMethod> callerMethods = calleeCallersPairs.removeAll(calleeMethod);
for (JMethod callerMethod : callerMethods) {
callerCalleesPairs.remove(callerMethod, calleeMethod);
}
return callerMethods;
}
/**
* Remove a caller method and all its callee methods in both
* {@code callerCalleesPairs} and {@code calleeCallersPairs}.
* Return its callee methods.
*/
public Set<JMethod> removeCallerMethod(JMethod callerMethod) {
Set<JMethod> calleeMethods = callerCalleesPairs.removeAll(callerMethod);
for (JMethod calleeMethod : calleeMethods) {
calleeCallersPairs.remove(calleeMethod, callerMethod);
}
return calleeMethods;
}
public void resetCallGraph() {
calleeCallersPairs.clear();
callerCalleesPairs.clear();
}
/**
* Update call graph of a JMethod. Record the deleted and added sub graph after the update in
* {@code deletedSubCallGraph} and {@code addedSubCallGraph} respectively.
*/
public void updateCallGraphOfMethod(JMethod method, CallGraph deletedSubCallGraph,
CallGraph addedSubCallGraph) {
Set<JMethod> calleeMethods = removeCallerMethod(method);
BuildCallGraphVisitor callSiteVisitor = new BuildCallGraphVisitor();
callSiteVisitor.accept(method);
deletedSubCallGraph.addCallerMethod(method,
Sets.difference(calleeMethods, callerCalleesPairs.get(method)));
addedSubCallGraph.addCallerMethod(method,
Sets.difference(callerCalleesPairs.get(method), calleeMethods));
}
}