/*
* 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.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.Set;
import polyglot.ast.Block;
import polyglot.ast.Call;
import polyglot.ast.CodeBlock;
import polyglot.ast.ConstructorDecl;
import polyglot.ast.MethodDecl;
import polyglot.types.MethodDef;
import polyglot.util.Position;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
import x10.ast.Closure;
import x10.compiler.ws.WSTransformState.CallSiteType;
import x10.compiler.ws.WSTransformState.MethodType;
/**
* @author Haichuan
* This is a container to store all information about WS transformation content.
* Including all target methods
* and all call sites
*
* Right now, we only use WSSourcePosition as precise match
* And we only support basic method decl. no constructor and closure support now
*
*
* Note, it is not thread safe. The thread safe is ensured by polyglot scheduler
*/
public class WSTransformationContent {
public class MethodAttribute {
protected String desc;
protected MethodType type;
protected boolean isDead; //the node is in dead code;
public MethodAttribute(String desc, MethodType type){
this.desc = desc;
this.type = type;
}
public boolean isDead() {
return isDead;
}
public void setDead(boolean isDead) {
this.isDead = isDead;
}
public MethodType getType() {
return type;
}
public String getDesc(){
return desc;
}
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append(getDesc());
sb.append(" : ");
sb.append(type);
if(isDead){
sb.append("[Dead]");
}
return sb.toString();
}
}
public class CallSiteAttribute {
protected String desc;
protected CallSiteType type;
public CallSiteAttribute(String desc, CallSiteType type){
this.desc = desc;
this.type = type;
}
public String getDesc() {
return desc;
}
public CallSiteType getType() {
return type;
}
public String toString(){
return getDesc() + " : " + type;
}
}
//Store the concurrent methods
//Note, the url has no header "file:" string, and the position is method body's position
//The String is method name, and [C] or [D]. C: contains concurrent, [D]: derived parallel
// (src url -> ( method body's position, method name))
protected TreeMap<String, Map<WSSourcePosition, MethodAttribute>> conMethodMap;
//Store all the call sites to concurrent methods
//Note, the url has no header "file:" string. and the position is call instruction's position
//The String is no clear usage now. Just store debug information now
// (src url -> ( call inst's pos, debug info)
protected TreeMap<String, Map<WSSourcePosition, CallSiteAttribute>> callSiteMap;
//Record all dead methods's def. These methods are not in call graph.
//So we cannot get their call sites directly.
//The content will be populated by a special barrier pass.
//In the pass, all methoddecl will be checked, if they are dead method decl (by source code line number)
//add the def in the set
//Then in the transformation pass, we check the call's method def', if it is in the deadMethoDefs, return matched call.
protected Set<MethodDef> deadMethodDefs;
public WSTransformationContent(){
conMethodMap = new TreeMap<String, Map<WSSourcePosition, MethodAttribute>>();
callSiteMap = new TreeMap<String, Map<WSSourcePosition, CallSiteAttribute>>();
deadMethodDefs = CollectionFactory.newHashSet();
}
public void addConcurrentMethod(String url, int startLine, int startColumn, int endLine, int endColumn, String info){
WSSourcePosition wsPosition = new WSSourcePosition(url, startLine, startColumn, endLine, endColumn);
Map<WSSourcePosition, MethodAttribute> innerMap;
if(conMethodMap.containsKey(url)){
innerMap = conMethodMap.get(url);
}
else{
innerMap = CollectionFactory.newHashMap();
conMethodMap.put(url, innerMap);
}
MethodAttribute ma = new MethodAttribute(info, MethodType.BODYDEF_TRANSFORMATION);
innerMap.put(wsPosition, ma);
}
/**
* @param url
* @param startLine
* @param startColumn
* @param endLine
* @param endColumn
* @param info
* @return the added method attributes if the node is a def only transformation node
*/
public MethodAttribute addImpactedMethod(String url, int startLine, int startColumn, int endLine, int endColumn, String info){
WSSourcePosition wsPosition = new WSSourcePosition(url, startLine, startColumn, endLine, endColumn);
Map<WSSourcePosition, MethodAttribute> innerMap;
if(conMethodMap.containsKey(url)){
innerMap = conMethodMap.get(url);
}
else{
innerMap = CollectionFactory.newHashMap();
conMethodMap.put(url, innerMap);
}
//check whether this node has been added. If not added, add it, and return it
if(!innerMap.containsKey(wsPosition)){
MethodAttribute ma = new MethodAttribute(info, MethodType.DEFONLY_TRANSFORMATION);
innerMap.put(wsPosition, ma);
return ma;
}
return null;
}
public void addConcurrentCallSite(String url, int startLine, int startColumn, int endLine, int endColumn, String info){
WSSourcePosition wsPosition = new WSSourcePosition(url, startLine, startColumn, endLine, endColumn);
Map<WSSourcePosition, CallSiteAttribute> innerMap;
if(callSiteMap.containsKey(url)){
innerMap = callSiteMap.get(url);
}
else{
innerMap = CollectionFactory.newHashMap();
callSiteMap.put(url, innerMap);
}
CallSiteAttribute ca = new CallSiteAttribute(info, CallSiteType.CONCURRENT_CALL);
innerMap.put(wsPosition, ca);
}
public void addMatchedCallSite(String url, int startLine, int startColumn, int endLine, int endColumn, String info){
WSSourcePosition wsPosition = new WSSourcePosition(url, startLine, startColumn, endLine, endColumn);
Map<WSSourcePosition, CallSiteAttribute> innerMap;
if(callSiteMap.containsKey(url)){
innerMap = callSiteMap.get(url);
}
else{
innerMap = CollectionFactory.newHashMap();
callSiteMap.put(url, innerMap);
}
if(!innerMap.containsKey(wsPosition)){
CallSiteAttribute ma = new CallSiteAttribute(info, CallSiteType.MATCHED_CALL);
innerMap.put(wsPosition, ma);
}
else{
System.err.println("[WALA_WS_ERR]One call site cannot be both concurrent call and matched call:"+ info);
}
}
/**
* Input a code block, return the method attribute
* If it is null. Not found.
* @param codeBlock method decl or interface
* @return the found method attribute object. If not found, null
*/
protected MethodAttribute getMethodAttribute(CodeBlock codeBlock){
Block methodBody = codeBlock.body();
Position pos;
if(methodBody != null){
pos = methodBody.position();
}
else{ //a interface
pos = codeBlock.position();
}
WSSourcePosition wsPos = new WSSourcePosition(pos);
//DEBUG
//System.out.printf("[MethodPos]%s: %s\n", mDecl, wsPos);
if(conMethodMap.containsKey(wsPos.getUrl())){
Map<WSSourcePosition, MethodAttribute> innerMap = conMethodMap.get(wsPos.getUrl());
if(innerMap.containsKey(wsPos)){
//DEBUG
//System.out.println(" Concurrent method info:" + innerMap.get(wsPos));
return innerMap.get(wsPos);
}
}
return null;
}
/**
* The input should be MethodDecl, ConstructorDecl, or Closure
* @param codeBlock
* @return whether the node is concurrent method
*/
public MethodType getMethodType(CodeBlock codeBlock){
MethodAttribute ma = getMethodAttribute(codeBlock);
if(ma != null){
return ma.getType();
}
return MethodType.NORMAL;
}
public void checkAndMarkDeadMethodDef(CodeBlock codeBlock){
MethodAttribute ma = getMethodAttribute(codeBlock);
if(ma != null && ma.isDead()){
//locate the method def from the code block
if(codeBlock instanceof MethodDecl){
deadMethodDefs.add(((MethodDecl)codeBlock).methodDef());
}
}
}
public CallSiteType getCallSiteType(Call call){
Position pos = call.position();
WSSourcePosition wsPos = new WSSourcePosition(pos);
//DEBUG
//System.out.printf("[CallSitePos]%s: %s\n", call, wsPos);
if(callSiteMap.containsKey(wsPos.getUrl())){
Map<WSSourcePosition, CallSiteAttribute> innerMap = callSiteMap.get(wsPos.getUrl());
if(innerMap.containsKey(wsPos)){
//DEBUG
//System.out.println(" Concurrent call site info:" + innerMap.get(wsPos));
return innerMap.get(wsPos).getType();
}
}
//And check the dead method def
MethodDef mDef = call.methodInstance().def();
if(deadMethodDefs.contains(mDef)){
return CallSiteType.MATCHED_CALL;
}
return CallSiteType.NORMAL;
}
/*
* Used to show the findings in CallGraph analysis
*/
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("-------------- WS CallGraph Analysis Result -------------\n");
//first iterate all concurrent method node
sb.append(">>>> Concurrent Method List\n");
for(String url : conMethodMap.keySet()){
Map<WSSourcePosition, MethodAttribute> innerMap = conMethodMap.get(url);
sb.append(" ").append(url).append('\n');
for(WSSourcePosition pos : innerMap.keySet()){
sb.append(" ").append(pos.toShortString()).append(" ").append(innerMap.get(pos)).append('\n');
}
}
sb.append('\n');
//then iterate all call sites
sb.append(">>>> Call Site to Concurrent Method List\n");
for(String url : callSiteMap.keySet()){
Map<WSSourcePosition, CallSiteAttribute> innerMap = callSiteMap.get(url);
sb.append(" ").append(url).append('\n');
for(WSSourcePosition pos : innerMap.keySet()){
sb.append(" ").append(pos.toShortString()).append(" ").append(innerMap.get(pos)).append('\n');
}
}
sb.append('\n');
return sb.toString();
}
}