/*
 * Menu: Indus > Mark Conflicting Writes
 * Script-Path: /Kaveri/monkey/ConflictingWrites.gm
 * Kudos: Venkatesh Prasad Ranganath (Santos Laboratory)
 * License: MPL 1.1
 * Job: UIJob
 * DOM: http://indus.projects.cis.ksu.edu/update/plugins/edu.ksu.cis.indus.kaveri
 * Include-Bundle: edu.ksu.cis.indus.kaveri
 */

 
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.resources.IMarker
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextSelection;
import groovy.lang.Closure;
import java.util.Collection;
import edu.ksu.cis.indus.common.datastructures.Pair;
import edu.ksu.cis.indus.common.soot.Util;
import edu.ksu.cis.indus.interfaces.IEscapeInfo;
import edu.ksu.cis.indus.staticanalyses.dependency.IDependencyAnalysis;
import edu.ksu.cis.indus.tools.slicer.SlicerTool;
import soot.jimple.Stmt;
import soot.jimple.AssignStmt;
import soot.jimple.InvokeStmt;
def BANNER = "Calculating conflicting writes for given write..."
def SRC_MARK_DESC = "Source write expression"
def DEST_MARK_DESC = "Conflicting write expression"
def EMPTY_SELECTION_MSG = "There are no field/array write expressions in the current selection."
def SOME_IN_BINARY_MSG = "There were some conflicting write in classes with no souce files."
def ENTITY_MSG = " conflicting writes."
def INVALID_SELECTION_MSG = "Please select a line containing a write expression in a Java file."
def EMPTY_RESULT_MSG = "There are no conflicting write expressions for the write expression(s) in the current selection."
boolean checkValidity(Stmt stmt) {
	return (stmt instanceof AssignStmt && 
      ((stmt.containsArrayRef() && stmt.getArrayRef().equals(stmt.getLeftOp())) || 
       (stmt.containsFieldRef() && stmt.getFieldRef().equals(stmt.getLeftOp()))))
}
boolean areRelated(sStmt, sMethod, dStmt, dMethod, escape) {
	if (checkValidity(dStmt)) {
		if (sStmt.containsArrayRef() && dStmt.containsArrayRef()) {
			return escape.areFieldAccessShared(sStmt.getArrayRef().getBase(), sMethod, dStmt.getArrayRef().getBase(), dMethod, IEscapeInfo.WRITE_WRITE_SHARED_ACCESS)
		} else if (sStmt.containsFieldRef() && dStmt.containsFieldRef()) {
			def sFieldRef = sStmt.getFieldRef();
			def dFieldRef = dStmt.getFieldRef();			
			def sField = sFieldRef.getField()
			def dField = dFieldRef.getField()
			if (sField.isStatic()) {
				return sField.equals(dField) && 
					escape.staticFieldAccessShared(sField.getDeclaringClass(), sMethod, IEscapeInfo.WRITE_WRITE_SHARED_ACCESS) &&
					escape.staticFieldAccessShared(dField.getDeclaringClass(), dMethod, IEscapeInfo.WRITE_WRITE_SHARED_ACCESS)
			} else {
				return sField.equals(dField) && 
			          escape.fieldAccessShared(sFieldRef.getBase(), sMethod, dFieldRef.getBase(), dMethod, IEscapeInfo.WRITE_WRITE_SHARED_ACCESS)
			}
		}
	}
	return false
}
// Customiable part ENDS.  Non-customizable part BEGINS.
Closure getClosure() {
	def escape = slicer.getEscapeInfo()
	closure = { sStmt, sMethod, monitor -> 
		def result = new HashSet()
		slicer.getCallGraph().getReachableMethods().each { dMethod ->
			if (dMethod.isConcrete()) {
				if (!dMethod.hasActiveBody()) {
					dMethod.retrieveActiveBody()
				}
				dMethod.getActiveBody().getUnits().each { dStmt ->
					if (areRelated(sStmt, sMethod, dStmt, dMethod, escape)) {							
						result << new Pair(dStmt, dMethod)
					}
				}
			}
		}
		return result
	}
	return closure
}
Collection getSrcExpressionsInSelection() {
	def r = slicer.getJimpleStmtsForSelection()
	def sootMethod = slicer.getJimpleMethodForSelection()
	def result = []
	r.each { stmt ->
		if (checkValidity(stmt)) {
			result << new Pair(stmt, sootMethod)
		}
	}
	return result
}
Collection getDestExprs(Collection exprs, NullProgressMonitor monitor) {
	def r = []
	def c = getClosure()
	exprs.each { expr -> 
	    if (monitor.isCanceled()) {
	      return r;
	    } else {
	      r += c.call(expr.getFirst(), expr.getSecond(), monitor)
	    }
	}
	return new HashSet(r)
}
void bookmarkSelection(message, ts) {
    def selection = slicer.getSelection()
    m = slicer.getSelectionContainingFile().createMarker(IMarker.BOOKMARK)
    m.setAttribute(IMarker.MESSAGE, "[" + ts + "] " + message)
    m.setAttribute(IMarker.LINE_NUMBER, selection.getStartLine() + 1) // text editor uses 0-offset addressing for lines
}
boolean validSelectionExists() {
	def f = slicer.getSelectionContainingFile()
	def s = slicer.getSelection()
	def n = f.getName()
	return s instanceof ITextSelection && (n.indexOf(".java") == (n.size() - 5))
}
if (slicer.getSlicerTool().isStable()) {
	def monitor = new NullProgressMonitor()
	monitor.beginTask(BANNER, IProgressMonitor.UNKNOWN)
	if (!monitor.isCanceled()) {
		if (validSelectionExists()) {
			def ts = slicer.getCounter()
			bookmarkSelection(SRC_MARK_DESC, ts)
			def srcExprs = getSrcExpressionsInSelection()
			if (srcExprs.isEmpty()) {
				slicer.displayDialog(EMPTY_SELECTION_MSG)
			} else {
				def i = 0
				def b = true
				def destExprs = getDestExprs(srcExprs, monitor)
				if (!destExprs.isEmpty()) {
					destExprs.each { destExpr ->
						if (monitor.isCanceled()) {
							return
						}
						b &= slicer.createMarkerForSootStmtMethod(destExpr, DEST_MARK_DESC, ts)
						i++
					}
				}
				if (i == 0) {
					slicer.displayDialog(EMPTY_RESULT_MSG)
				} else {
					def msg = 'There were total of ' + i + ENTITY_MSG
					if (!b) {
						msg += SOME_IN_BINARY_MSG
					}
					slicer.displayDialog(msg)
					window.getActivePage().showView("org.eclipse.ui.views.BookmarkView")
				}
			}
		} else {
			slicer.displayDialog(INVALID_SELECTION_MSG)
		}
	}
	monitor.done()
} else {
	slicer.displayDialog("Please run the slicer before executing this script.")
}