All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.toolkits.annotation.purity.PurityInterproceduralAnalysis Maven / Gradle / Ivy

There is a newer version: 2.5.0-9
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 2005 Antoine Mine
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * Implementation of the paper "A Combined Pointer and Purity Analysis for
 * Java Programs" by Alexandru Salcianu and Martin Rinard, within the
 * Soot Optimization Framework.
 *
 * by Antoine Mine, 2005/01/24
 */

package soot.jimple.toolkits.annotation.purity;
import java.util.*;
import soot.*;
import soot.util.dot.*;
import soot.jimple.*;
import soot.jimple.toolkits.callgraph.*;
import soot.toolkits.graph.*;
import soot.options.PurityOptions;
import soot.tagkit.*;

public class PurityInterproceduralAnalysis 
    extends AbstractInterproceduralAnalysis {

    // Note: these method lists are adapted to JDK-1.4.2.06 and may
    // not work for other versions

    // unanalysed methods assumed pure (& return a new obj)
    // class name prefix / method name
    static private final String[][] pureMethods =
    { 	{"java.lang.","valueOf"},
	{"java.","equals"},{"javax.","equals"},{"sun.","equals"},
	{"java.","compare"}, {"javax.","compare"},{"sun.","compare"},
	{"java.","getClass"},{"javax.","getClass"},{"sun.","getClass"},
	{"java.","hashCode"},{"javax.","hashCode"},{"sun.","hashCode"},
	{"java.","toString"},{"javax.","toString"},{"sun.","toString"},
	{"java.","valueOf"},{"javax.","valueOf"},{"sun.","valueOf"},
	{"java.","compareTo"},{"javax.","compareTo"},{"sun.","compareTo"},

	{"java.lang.System","identityHashCode"},

	// we assume that all standard class initialisers are pure!!!
	{"java.",""}, {"javax.",""}, {"sun.",""},
	
	// if we define these as pure, the analysis will find them impure as
	// they call static native functions that could, in theory,
	// change the whole program state under our feets
	{ "java.lang.Math","abs" },
	{ "java.lang.Math","acos" },
	{ "java.lang.Math","asin" },
	{ "java.lang.Math","atan" },
	{ "java.lang.Math","atan2" },
	{ "java.lang.Math","ceil" },
	{ "java.lang.Math","cos" },
	{ "java.lang.Math","exp" },
	{ "java.lang.Math","floor" },
	{ "java.lang.Math","IEEEremainder" },
	{ "java.lang.Math","log" },
	{ "java.lang.Math","max" },
	{ "java.lang.Math","min" },
	{ "java.lang.Math","pow" },
	{ "java.lang.Math","rint" },
	{ "java.lang.Math","round" },
	{ "java.lang.Math","sin" },
	{ "java.lang.Math","sqrt" },
	{ "java.lang.Math","tan" },
	// TODO: put StrictMath as well ?

	{"java.lang.Throwable", ""},

	// to break the cycle exception -> getCharsAt -> exception
	{"java.lang.StringIndexOutOfBoundsException",""}
    };

    // unanalysed methods that modify the whole environment
    static private final String[][] impureMethods =
    { 
	{"java.io.",""},
	{"java.io.","close"},
	{"java.io.","read"},
	{"java.io.","write"},
	{"java.io.","flush"}, 
	{"java.io.","flushBuffer"}, 
	{"java.io.","print"}, 
	{"java.io.","println"}, 

 	{"java.lang.Runtime","exit"},

	/*
	// for soot...
	{"java.io.","skip"},
	{"java.io.","ensureOpen"},
	{"java.io.","fill"},
	{"java.io.","readLine"},
	{"java.io.","available"},
	{"java.io.","mark"},
	{"java.io.","reset"},
	{"java.io.","toByteArray"},
	{"java.io.","size"},
	{"java.io.","writeTo"},
	{"java.io.","readBoolean"},
	{"java.io.","readChar"},
	{"java.io.","readDouble"},
	{"java.io.","readFloat"},
	{"java.io.","readByte"},
	{"java.io.","readShort"},
	{"java.io.","readInt"},
	{"java.io.","readLong"},
	{"java.io.","readUnsignedByte"},
	{"java.io.","readUnsignedShort"},
	{"java.io.","readUTF"},
	{"java.io.","readFully"},
	{"java.io.","writeBoolean"},
	{"java.io.","writeChar"},
	{"java.io.","writeChars"},
	{"java.io.","writeDouble"},
	{"java.io.","writeFloat"},
	{"java.io.","writeByte"},
	{"java.io.","writeBytes"},
	{"java.io.","writeShort"},
	{"java.io.","writeInt"},
	{"java.io.","writeLong"},
	{"java.io.","writeUTF"},
	{"java.io.","canRead"},
	{"java.io.","delete"},
	{"java.io.","exists"},
	{"java.io.","isDirectory"},
	{"java.io.","isFile"},
	{"java.io.","mkdir"},
	{"java.io.","mkdirs"},
	{"java.io.","getAbsoluteFile"},
	{"java.io.","getCanonicalFile"},
	{"java.io.","getParentFile"},
	{"java.io.","listFiles"},
	{"java.io.","getAbsolutePath"},
	{"java.io.","getCanonicalPath"},
	{"java.io.","getName"},
	{"java.io.","getParent"},
	{"java.io.","getPath"},
	{"java.io.","list"},
	{"java.io.","toURI"},
	{"java.io.","lastModified"},
	{"java.io.","length"},
	{"java.io.","implies"},
	{"java.io.","newPermissionCollection"},
	{"java.io.","getLineNumber"},
	{"java.io.","enableResolveObject"},
	{"java.io.","readClassDescriptor"},
	{"java.io.","readFields"},
	{"java.io.","readObject"},
	{"java.io.","readUnshared"},
	{"java.io.","defaultReadObject"},
	{"java.io.","defaultWriteObject"},
	{"java.io.","putFields"},
	{"java.io.","writeFields"},
	{"java.io.","writeObject"},
	{"java.io.","writeUnshared"},
	{"java.io.","unread"},
	{"java.io.","lineno"},
	{"java.io.","nextToken"},
	{"java.io.","commentChar"},
	{"java.io.","lowerCaseMode"},
	{"java.io.","ordinaryChar"},
	{"java.io.","quoteChar"},
	{"java.io.","resetSyntax"},
	{"java.io.","slashSlashComments"},
	{"java.io.","slashSltarComments"},
	{"java.io.","whitespaceChars"},
	{"java.io.","wordChars"},
	{"java.io.","markSupported"},
	{"java.","getCause"},
	{"java.","getMessage"},
	{"java.","getReason"},
	*/

    };
    
    // unanalysed methods that alter its arguments, but have no side effect
    static private final String[][] alterMethods =
    { 
	{"java.lang.System","arraycopy"},

	// these are really huge methods used internally by StringBuffer
	// printing => put here to speed-up the analysis
	{"java.lang.FloatingDecimal","dtoa"},
	{"java.lang.FloatingDecimal","developLongDigits"},
	{"java.lang.FloatingDecimal","big5pow"},
	{"java.lang.FloatingDecimal","getChars"},
	{"java.lang.FloatingDecimal","roundup"},
    };

    /** Filter out some method. */
    static private class Filter implements SootMethodFilter {
	public boolean want(SootMethod method) { 
	    // could be optimized with HashSet....
	    String c = method.getDeclaringClass().toString();
	    String m = method.getName();
	    String[][] o = PurityInterproceduralAnalysis.pureMethods;
	    for (String[] element : o)
			if (m.equals(element[1]) && c.startsWith(element[0])) return false;
	    o = PurityInterproceduralAnalysis.impureMethods;
	    for (String[] element : o)
			if (m.equals(element[1]) && c.startsWith(element[0])) return false;
	    o = PurityInterproceduralAnalysis.alterMethods;
	    for (String[] element : o)
			if (m.equals(element[1]) && c.startsWith(element[0])) return false;
	    return true;
	}
    }
    
    /** The constructor does it all! */
    PurityInterproceduralAnalysis(CallGraph        cg,
				  Iterator         heads,
				  PurityOptions    opts)
    {
	super(cg,new Filter(),heads, opts.dump_cg());
	
	if (opts.dump_cg()) {
	    G.v().out.println("[AM] Dumping empty .dot call-graph");
	    drawAsOneDot("EmptyCallGraph");
	}

	Date start = new Date();
	G.v().out.println("[AM] Analysis began");
	doAnalysis(opts.verbose());
	G.v().out.println("[AM] Analysis finished");
	Date finish = new Date();
	long runtime = finish.getTime() - start.getTime();
	G.v().out.println("[AM] run time: "+runtime/1000.+" s");

	if (opts.dump_cg()) {
	    G.v().out.println("[AM] Dumping annotated .dot call-graph");
	    drawAsOneDot("CallGraph");
	}

	if (opts.dump_summaries()) {
	    G.v().out.println("[AM] Dumping .dot summaries of analysed methods");
	    drawAsManyDot("Summary_",false);
	}

	if (opts.dump_intra()) {
	    G.v().out.println("[AM] Dumping .dot full intra-procedural method analyses");
	    // relaunch the interprocedural analysis once on each method
	    // to get a purity graph at each statement, not only summaries
	    Iterator it = getAnalysedMethods();
	    while (it.hasNext()) {
		SootMethod method = (SootMethod)it.next();
		Body body = method.retrieveActiveBody();
		ExceptionalUnitGraph graph = new ExceptionalUnitGraph(body);
		if (opts.verbose()) G.v().out.println("  |- "+method);
		PurityIntraproceduralAnalysis r = 
		    new PurityIntraproceduralAnalysis(graph, this);
		r.drawAsOneDot("Intra_",method.toString());
		PurityGraphBox b = new PurityGraphBox();
		r.copyResult(b);
	    }
	}


	{
	    G.v().out.println("[AM] Annotate methods. ");
	    Iterator it = getAnalysedMethods();
	    while (it.hasNext()) {
		SootMethod m = (SootMethod)it.next();
		PurityGraphBox b = (PurityGraphBox)getSummaryFor(m);

		// purity
		boolean isPure;
		if (m.toString().indexOf("")!=-1)
		    isPure = b.g.isPureConstructor() ;
		else 
		    isPure = b.g.isPure();
		/* m.addTag(new GenericAttribute("isPure",
		   (new String(isPure?"yes":"no")).getBytes()));
		*/
		m.addTag(new StringTag("purity: "+(isPure?"pure":"impure")));
		if(isPure && opts.annotate())
			m.addTag(new GenericAttribute("Pure", new byte[0]));
		if (opts.print()) 
		    G.v().out.println("  |- method "+m.toString()+" is "+(isPure?"pure":"impure"));

		// param & this ro / safety
		if (!m.isStatic()) {
		    int status = b.g.thisStatus();
		    String s;
		    switch (status) {
		    case PurityGraph.PARAM_RW: s = "read/write";break;
		    case PurityGraph.PARAM_RO: s = "read-only";break;
		    case PurityGraph.PARAM_SAFE: s = "Safe";break;
		    default: s = "unknown";
		    }
		    /*  m.addTag(new GenericAttribute("thisStatus",s.getBytes()));
		     */
		    m.addTag(new StringTag("this: "+s));
		    if (opts.print()) G.v().out.println("  |   |- this is "+s);
		}
		
		Iterator itt = m.getParameterTypes().iterator();
		int i = 0;
		while (itt.hasNext()) {
		    if (itt.next() instanceof RefLikeType) {
			int status = b.g.paramStatus(i);
			String s;
			switch (status) {
			case PurityGraph.PARAM_RW: s = "read/write";break;
			case PurityGraph.PARAM_RO: s = "read-only";break;
			case PurityGraph.PARAM_SAFE: s = "safe";break;
			default: s = "unknown";
			}
			/*
			  m.addTag(new GenericAttribute("param"+i+"Status",
			  s.getBytes()));
			*/
			m.addTag(new StringTag("param"+i+": "+s));
			if (opts.print())
			    G.v().out.println("  |   |- param "+i+" is "+s);
		    }
		    i++;
		}
	    }
	}

    }

    protected Object newInitialSummary()
    { return new PurityGraphBox(); }

    protected void merge(Object in1, Object in2, Object out)
    {
	PurityGraphBox i1  = (PurityGraphBox)in1;
	PurityGraphBox i2  = (PurityGraphBox)in2;
	PurityGraphBox o   = (PurityGraphBox)out;
	if (out!=i1) o.g = new PurityGraph(i1.g);
	o.g.union(i2.g);
    }

    protected void copy(Object source, Object dest)
    {
	PurityGraphBox src  = (PurityGraphBox)source;
	PurityGraphBox dst  = (PurityGraphBox)dest;
	dst.g = new PurityGraph(src.g);
    }
    
    protected void analyseMethod(SootMethod method,
				 Object     dst)
    {
	Body body = method.retrieveActiveBody();
	ExceptionalUnitGraph graph = new ExceptionalUnitGraph(body);
	PurityIntraproceduralAnalysis r = 
	    new PurityIntraproceduralAnalysis(graph, this);
	r.copyResult(dst);
    }

    /**
     * @see PurityGraph.conservativeGraph
     * @see PurityGraph.freshGraph
     */
    protected Object summaryOfUnanalysedMethod(SootMethod method)
    {
	PurityGraphBox b = new PurityGraphBox();
	String c = method.getDeclaringClass().toString();
	String m = method.getName();

	// impure with side-effect, unless otherwise specified
	b.g = PurityGraph.conservativeGraph(method,true);

	String[][] o = PurityInterproceduralAnalysis.pureMethods;
	for (String[] element : o)
		if (m.equals(element[1]) && c.startsWith(element[0]))
		b.g = PurityGraph.freshGraph(method);

	o = PurityInterproceduralAnalysis.alterMethods;
	for (String[] element : o)
		if (m.equals(element[1]) && c.startsWith(element[0]))
		b.g = PurityGraph.conservativeGraph(method,false);

	return b;
    }

    /**
     * @param stmt any statement containing an InvokeExpr
     * @see PurityGraph.methodCall
     */
    protected void applySummary(Object src,
				Stmt   stmt,
				Object summary,
				Object dst)
    {
	// extract call info
	InvokeExpr e = stmt.getInvokeExpr();
	Local ret = null;
	if (stmt instanceof AssignStmt) {
	    Local v = (Local)((AssignStmt)stmt).getLeftOp();
	    if (v.getType() instanceof RefLikeType) ret = v;
	}
	Local obj = null;
	if (!(e instanceof StaticInvokeExpr)) 
	    obj = (Local)((InstanceInvokeExpr)e).getBase();
	List args = e.getArgs();
	
	// call methoCall on the PurityGraph
	PurityGraphBox s = (PurityGraphBox)src;
	PurityGraphBox d = (PurityGraphBox)dst;
	PurityGraph g = new PurityGraph(s.g);
	g.methodCall(((PurityGraphBox)summary).g, obj, args, ret);
	d.g = g;
    }

    protected void fillDotGraph(String prefix, Object o, DotGraph out)
    {
	PurityGraphBox b = (PurityGraphBox)o;
	b.g.fillDotGraph(prefix, out);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy