/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.compiler.ws.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import polyglot.ast.Call;
import polyglot.ast.ClassDecl;
import polyglot.ast.ConstructorDecl;
import polyglot.ast.MethodDecl;
import polyglot.ast.Node;
import polyglot.ast.Term;
import polyglot.main.Report;
import polyglot.types.ClassDef;
import polyglot.types.FunctionDef;
import polyglot.types.MemberDef;
import polyglot.types.Package;
import polyglot.types.ProcedureDef;
import polyglot.types.Ref;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.visit.NodeVisitor;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import x10.ast.Closure;
import x10.ast.PlacedClosure;
/**
* A call graph to record WS transformation call graph and do DFS search
* It contains method, constructor, and closure
*
*
* @author Haichuan
*
*/
public class WSCallGraph {
static final String ignorePackages[] = {
"x10",
};
static final String ignoreClasses[] ={
"AsyncFrame",
"BoxedBoolean",
"Continuation",
"FinishFrame",
"Frame",
"MainFrame",
"RegularFrame",
"WhenFrame",
"Worker",
"Deque",
};
static public boolean ignoreClass(ClassDef classDef){
Ref<? extends Package> pRef = classDef.package_();
if(pRef != null){
Package p = pRef.get();
String pName = p.toString();
for(String s : ignorePackages){
if(pName.startsWith(s)){
return true;
}
}
}
String className = classDef.toString();
for(String s : ignoreClasses){
if(className.equals(s)){
return true;
}
}
return false;
}
static final int debugLevel = 0;
protected Map<ProcedureDef, WSCallGraphNode> def2NodeMap;
//These methods has parallel constructs in them;
protected List<WSCallGraphNode> initialParallelMethods;
public WSCallGraph(){
def2NodeMap = CollectionFactory.newHashMap();
initialParallelMethods = new ArrayList<WSCallGraphNode>();
}
public WSCallGraphNode findOrCreateNode(ProcedureDef methodDef){
if(def2NodeMap.containsKey(methodDef)){
return def2NodeMap.get(methodDef);
}
WSCallGraphNode node = new WSCallGraphNode(this, methodDef);
def2NodeMap.put(methodDef, node);
return node;
}
public boolean addClass(ClassDecl classDecl){
if(ignoreClass(classDecl.classDef())){
if(debugLevel > 3){
System.out.println("[WS_INFO]WSCallGraph: Ingore Class: " + classDecl);
}
return false;
}
//typically, it only analyze top level class, inner class is combined into its container to analysis
NodeVisitor v = new NodeVisitor(){
public Node leave(Node old, Node n, NodeVisitor v) {
if(n instanceof MethodDecl
|| n instanceof ConstructorDecl
|| (n instanceof Closure && !(n instanceof PlacedClosure))){
//Note, PlacedClosure are not treated as normal closure, not build node
addProcedure((Term)n);
}
return n;
}
};
classDecl.visit(v);
return true;
}
/**
* used only by WSCodePreprocessor.
* In the pass, it may generate some concurrent method.
* @param mDecl
*/
public void addSynthesizedConcurrentMethod(MethodDecl mDecl) {
WSCallGraphNode node = findOrCreateNode(mDecl.methodDef());
node.setParallel(true);
}
/**
* Add a method into the node, and mark the parallel status
* And build the call graph, too
*
* @param method
*/
public void addProcedure(Term procedure){
ProcedureDef pDef = null;
if(procedure instanceof MethodDecl){
pDef = ((MethodDecl)procedure).methodDef();
}
else if(procedure instanceof ConstructorDecl){
pDef = ((ConstructorDecl)procedure).constructorDef();
}
else if(procedure instanceof Closure){
pDef = ((Closure)procedure).closureDef();
}
WSCallGraphNode node = findOrCreateNode(pDef);
//otherwise depend on the current one
if(!node.isCallgraphBuild()){ //prevent add one method again
//now check whether this method has parallel;
if(WSUtil.containsConcurrentConstruct(procedure)){
node.setContainsConcurrent(true);
initialParallelMethods.add(node);
}
//now get all callees from this node
node.addCallTo(WSUtil.scanForCallees(procedure));
// //debug info
// System.out.println("scan for callto:"+pDef);
// for(ProcedureDef pcDef : WSCodeGenUtility.scanForCallees(procedure)){
// System.out.println(" "+pcDef);
// }
}
}
protected String getJavaSignature(ProcedureDef pDef){
StringBuffer sb = new StringBuffer();
if(pDef instanceof MemberDef){
MemberDef mDef = (MemberDef)pDef;
String containerStr = mDef.container().get().toString().replace('.', '/');
sb.append(containerStr).append('.');
}
else{
//closure type is not well supported yet
}
//process signature
String sig = pDef.signature();
int b1Index = sig.indexOf('(');
int b2Index = sig.indexOf(')');
//method name
sb.append(sig.substring(0, b1Index)).append('(');
String formalTypes[] = sig.substring(b1Index+1, b2Index).split(",");
for(String fStr : formalTypes){
String ftStr = fStr.trim();
if(ftStr.length() == 0){
continue;
}
sb.append(type2JavaSignature(ftStr));
}
sb.append(')');
//process return types
if(pDef instanceof FunctionDef){
FunctionDef fDef = (FunctionDef)pDef;
String rStr = fDef.returnType().get().toString();
if(rStr == null || rStr.length() == 0 || rStr.equalsIgnoreCase("void") ){
sb.append('V');
}
else{
sb.append('L').append(rStr.replace('.', '/')).append(';');
}
}
return sb.toString();
}
/**
* Input format: n:x10.lang.Int args:x10.array.Array[x10.lang.String]{self.rank==1}
* or no name, just type
* @param type
* @return Lx10/lang/Int;
*/
protected String type2JavaSignature(String typeStr){
int sIndex = Math.max(0, typeStr.indexOf(':') + 1);
//search template '['
int tIndex = typeStr.indexOf('[');
tIndex = tIndex > 0 ? tIndex : Integer.MAX_VALUE;
//search constraint '{'
int cIndex = typeStr.indexOf('{');
cIndex = cIndex > 0 ? cIndex : Integer.MAX_VALUE;
int eIndex = Math.min(typeStr.length(), Math.min(tIndex, cIndex));
StringBuffer sb = new StringBuffer();
sb.append('L').append(typeStr.substring(sIndex, eIndex).replace('.', '/')).append(';');
return sb.toString();
}
/**
* Mark all possible methods as target methods.
* Start from those contains concurrent constructs, and do DFS search to mark.
*/
public void doDFSMark(){
for(WSCallGraphNode node : initialParallelMethods){
doDFSMark(node);
}
}
protected void doDFSMark(WSCallGraphNode node){
node.setParallel(true); //in case future clean;
//then depths first;
for(WSCallGraphNode parent :node.getCallers()){
if(!parent.isParallel()){
doDFSMark(parent);
}
//if parent is indeed, someone else is marked, no need mark again.
}
}
/**
* Return all parallel method from this call graph, including those from DFS search
* @return
*/
public List<WSCallGraphNode> getAllParallelMethods(){
List<WSCallGraphNode> methods = new ArrayList<WSCallGraphNode>();
for(WSCallGraphNode node : def2NodeMap.values()){
if(node.isParallel()){
methods.add(node);
}
}
return methods;
}
public boolean isParallel(ProcedureDef m) {
WSCallGraphNode n = def2NodeMap.get(m);
if(n == null){
if(m instanceof MemberDef){
Type type = ((MemberDef)m).container().get();
if(type.toString().startsWith("x10.")){
return false; //not report x10 pacakges;
}
}
System.out.println("[WS_WARNING]ProcedureDef:" + m + " was not added to call graph. Suppose it is not a parallel procedure.");
return false;
}
else{
return n.parallel;
}
}
}