[Soot-list] Return Stmt

Chris Pickett chris.pickett at mail.mcgill.ca
Thu May 10 23:37:10 EDT 2007


Mario said "block" and not "body" but I'm still not sure that the 
invariant holds.  You can test it by inserting a statement after a 
return, writing out the class file, reading it back into Soot, and 
seeing if it's still there.  Quite possibly a dead code elimination pass 
will get rid of it, but that pass could be turned off...

Chris

Eric Bodden wrote:
> No, sorry but that's not at all true. There can be finitely many
> return statements per body. However, the abc compiler
> (www.aspectbench.org) has the class Restructure that implements the
> method restructureReturn(SootMethod). After applying this method, the
> invariant holds. I have the class attached for your conveninence.
> However, please respect the appropriate licensing terms in the header
> (LGPL) if making use of it.
> 
> Cheers,
> Eric
> 
> On 10/05/07, Mario Mendez <mario at cs.unm.edu> wrote:
>> Can your confirm that the invariant "if a return statement is present in
>> a block, it is always the last stmt of that block" is true?
>>
>> As always,thanks!
>> _______________________________________________
>> Soot-list mailing list
>> Soot-list at sable.mcgill.ca
>> http://mailman.cs.mcgill.ca/mailman/listinfo/soot-list
>>
> 
> 
> 
> ------------------------------------------------------------------------
> 
> /* abc - The AspectBench Compiler
>  * Copyright (C) 2004 Laurie Hendren
>  * Copyright (C) 2004 Ondrej Lhotak
>  * Copyright (C) 2004 Jennifer Lhotak
>  * Copyright (C) 2004 Sascha Kuzins
>  * Copyright (C) 2004 Ganesh Sittampalam
>  *
>  * This compiler 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 compiler 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 compiler, in the file LESSER-GPL;
>  * if not, write to the Free Software Foundation, Inc.,
>  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
>  */
> 
> package abc.soot.util;
> 
> import java.util.*;
> 
> import polyglot.util.InternalCompilerError;
> 
> import soot.*;
> import soot.util.*;
> import soot.jimple.*;
> import soot.javaToJimple.LocalGenerator;
> import soot.jimple.toolkits.scalar.*;
> 
> import abc.weaving.weaver.*;
> import abc.weaving.aspectinfo.MethodCategory;
> 
> 
> 
> /** This class contains a variety of helper methods to restructure Soot
>  *    method Bodies.
>  *
>  * @author Laurie Hendren
>  * @author Ondrej Lhotak
>  * @author Jennifer Lhotak
>  * @author Sascha Kuzins
>  * @author Ganesh Sittampalam
>  */
> 
> public class Restructure {
> 
>     public static void reset() {
> 	//returns=new Hashtable();
> 	//thiscopies=new Hashtable();
> 	invokeassignstmts=new Hashtable();
>     }
> 
>    private static void debug(String message)
>      { if (abc.main.Debug.v().restructure)
>          System.err.println("RST*** " + message);
>      }
> 
>    /* ------------------- Utility restructurers ----------------------- */
> 
>   /** Given a Chain for a body of an <init> method, find the call to
>    *  the <init> corresponding to either a this() or super() call.  Check
>    *  that there is exactly one such <init>, otherwise throw a 
>    *  InternalCompilerError.   Return the <init> Stmt.
>    */
>   public static InvokeStmt findInitStmt(Chain units)
>     { // look for the <init> 
>       Iterator it = units.snapshotIterator();
>       InvokeStmt initstmt = null;
> 
>       // need to track all locals containing refs to "this"
>       LinkedList receivercopies = new LinkedList();
> 
>       // get the "this", should be first identity statement
>       Stmt first = (Stmt) it.next();
>       Local thisloc = null;
>       if ( (first instanceof IdentityStmt) && 
> 	   ((IdentityStmt) first).getRightOp() instanceof ThisRef
> 	 )
>         { thisloc = (Local) 
> 	        ((IdentityStmt) first).getLeftOp();//the local for "this" 
>           // add to list of locals containing this
>           receivercopies.add(thisloc);
> 	}
> 	else
> 	  throw new InternalCompilerError("Expecting an identity stmt for this");
> 	
>       int countinits = 0;
>       debug("--- Starting to look through statement list ..... ");
>       while ( it.hasNext() )
>         { Stmt u = (Stmt) it.next();
>           debug(" ... Looking at stmt " + u);
> 
> 	  // if we find a stmt lhs = rhs, where rhs is already a copy
> 	  //     of "this",  add lhs to receivercopies
> 	  if ((u instanceof AssignStmt) &&
> 	      receivercopies.contains(((AssignStmt) u).getRightOp()))
> 	    receivercopies.add(((AssignStmt) u).getLeftOp());
> 
>           if ((u instanceof InvokeStmt) && 
> 	      ((InvokeStmt) u).getInvokeExpr() instanceof SpecialInvokeExpr &&
> 	      ((InvokeStmt) u).getInvokeExpr().getMethodRef().name()
> 	      .equals(SootMethod.constructorName) &&
> 	      receivercopies.contains
> 	      (((SpecialInvokeExpr) ((InvokeStmt) u).getInvokeExpr()).getBase()))
> 	    { debug("Found <init> " + u);
> 	      countinits++;
> 	      if (countinits == 1) // great, found it
> 	        initstmt = (InvokeStmt) u;  
> 	      else
> 	        throw new InternalCompilerError("Expecting only one <init>");
>              }	 
>           } // all units
> 	 
>        debug("--- Finished looking through statement list ..... ");
>        if (countinits == 0)     
>          throw new InternalCompilerError("Could not find a matching <init>");
> 
>        return(initstmt);
>      }
> 
>    /** Given a Chain for the body of a method,  find the first "real"
>     *  stmt (i.e. not an identity stmt or the copy of "this" we often make) 
>     *  and return a reference to it.
>     */
>     public static Stmt findFirstRealStmt(SootMethod m,Chain units)
>     {
> 	return findFirstRealStmt(m,units,false);
>     }
> 
>     public static Stmt findFirstRealStmtOrNop(SootMethod m,Chain units)
>     {
> 	return findFirstRealStmt(m,units,true);
>     }
> 
>     private static Stmt findFirstRealStmt(SootMethod m,Chain units,boolean allowNops)
>      { Iterator it = units.snapshotIterator();
>        while ( it.hasNext() ) { 
> 	     Stmt u = (Stmt) it.next();
> 	     if(u instanceof IdentityStmt) continue;
> 	     if(u instanceof NopStmt && !allowNops) continue;
> 	     // skip over any copy of "this" we made
> 	     //if(u instanceof AssignStmt)
> 		 //if(thiscopies.containsKey(m)) 
> 		   //  if(((AssignStmt) u).getLeftOp()==thiscopies.get(m))
> 			 //continue;
> 	     return u;
>           }
>         throw new InternalCompilerError("Expecting to find a real stmt");
>       }
>     public static Stmt findFirstRealStmtOrNull(SootMethod m,Chain units)
>     { 
>     	Iterator it = units.iterator();
>       while ( it.hasNext() ) { 
> 	     Stmt u = (Stmt) it.next();
> 	     if(u instanceof IdentityStmt) 
> 	     	continue;
> 	     return u;
>        }
>        return null;
>      }
>    /** update all traps that used to end at oldend to now end at newend
>     */
>    public static void resetTrapsEnd(Body b, Stmt oldend, Stmt newend)
>      { for( Iterator trIt = b.getTraps().iterator(); trIt.hasNext(); ) 
>          { final Trap tr = (Trap) trIt.next();
>            if( tr.getEndUnit() == oldend ) 
> 	     tr.setEndUnit(newend);  
> 	  }
>       }
> 
>    /** update all traps that used to start at oldstart to now start at newstart
>     */
>    public static void resetTrapsStart(Body b, Stmt oldstart, Stmt newstart)
>      { for( Iterator trIt = b.getTraps().iterator(); trIt.hasNext(); ) 
>          { final Trap tr = (Trap) trIt.next();
>            if( tr.getBeginUnit() == oldstart ) 
> 	     tr.setBeginUnit(newstart);  
> 	  }
>       }
> 
>    // This caching is not safe because around lifts shadows out of their bodies,
>    // in which case the restructuring should be redone for subsequent applications.
>   //private static Map/*<Body,Stmt>*/ returns=new Hashtable();
> 
>   /** Given a SootMethod, restructure its body so that the body ends
>    *  with   L1:nop; return;    or   L1:nop; return(<local>);.
>    *  Rewire all other returns in the body to assign to <local> and
>    *  goto L1.   Return a reference to the nop at L1.
>    */
>   public static Stmt restructureReturn(SootMethod method) {
> 		Body b = method.getActiveBody();
> 		//if (returns.containsKey(b)) {			
> 		//	return ((Stmt) returns.get(b));
> 		//}
> 		LocalGenerator localgen = new LocalGenerator(b);
> 		
> 		Chain units = b.getUnits(); // want a patching chain here, to make sure
> 		// gotos are rewired to go to the inserted nop
> 		Stmt last = (Stmt) units.getLast();
> 		
> 		Stmt endnop;// = Jimple.v().newNopStmt();
> 		try { // preserve existing endnop
> 			endnop=(NopStmt)units.getPredOf(last);
> 			if (endnop==null)
> 				throw new RuntimeException();
> 		} catch(Throwable e) {
> 			endnop=Jimple.v().newNopStmt();
> //			 insert the nop just before the return stmt
> 			if (last instanceof ReturnStmt || last instanceof ReturnVoidStmt) {
> 				units.insertBefore(endnop, units.getLast());
> 			} else {
> 				units.insertAfter(endnop, units.getLast());
> 			}
> 			if (!units.contains(endnop))
> 				throw new InternalCompilerError("");
> 		}
> 		if (!units.contains(endnop))
> 			throw new InternalCompilerError("");
> 		
> 		Local ret = null;
> 		if (last instanceof ReturnStmt) {
> 			ReturnStmt lastRet = (ReturnStmt) last;
> 			Value op = lastRet.getOp();
> 			if (op instanceof Local) {// return(<local>)
> 				ret = (Local)op;
> 				Type returnType = method.getReturnType();
> 				Local tmp = localgen.generateLocal(returnType);
> 				// Make sure returned local is used in the shadow once
> 				// by assigning it to a temporary and back.
> 				// This makes the around weaver's analyses simpler.			
> 				units.insertBefore(Jimple.v().newAssignStmt(tmp, op), endnop);
> 				units.insertBefore(Jimple.v().newAssignStmt(op, tmp), endnop);
> 			} else if (op instanceof Constant) // return(<constant>)
> 			{ // change to ret := <constant>; return(ret);
> 				Type returnType = method.getReturnType();
> 				ret = localgen.generateLocal(returnType);
> 				if (!units.contains(endnop))
> 					throw new InternalCompilerError("");
> 				units.insertBefore(Jimple.v().newAssignStmt(ret, op), endnop);
> 				lastRet.setOp(ret);
> 			} else
> 				throw new InternalCompilerError(
> 						"Expecting return of <local> or <constant>");
> 		} else if (last instanceof ReturnVoidStmt) { // do nothing
> 		} else {
> 			Type returnType = method.getReturnType();
> 			if (returnType instanceof VoidType) {
> 				units.insertAfter(Jimple.v().newReturnVoidStmt(), endnop);
> 			} else {
> 				ret = localgen.generateLocal(returnType);
> 				units.insertAfter(Jimple.v().newReturnStmt(ret), endnop);
> 			}
> 		}
> 
> 		// now the last stmt should always be return ret; or return;
> 		if (units.getLast() instanceof ReturnStmt) {
> 			ReturnStmt lastRet = (ReturnStmt) units.getLast();
> 			if (lastRet.getOp() != ret)
> 				throw new InternalCompilerError(
> 						"Expecting last stmt to Return ret");
> 		} else if (!(units.getLast() instanceof ReturnVoidStmt)) {
> 			throw new InternalCompilerError(
> 					"Last stmt should be ReturnStmt or ReturnVoidStmt");
> 		}
> 
> 		
> 		
> 
> 		resetTrapsEnd(b, (Stmt) units.getLast(), endnop);
> 
> 		// Look for returns in the middle of the method
> 		Iterator it = units.snapshotIterator();
> 		while (it.hasNext()) {
> 			Stmt u = (Stmt) it.next();
> 			if (u == units.getLast())
> 				continue;
> 			if (u instanceof ReturnStmt) {
> 				ReturnStmt ur = (ReturnStmt) u;
> 				units.insertBefore(Jimple.v().newAssignStmt(ret, ur.getOp()),
> 						ur);
> 			}
> 			if (u instanceof ReturnVoidStmt || u instanceof ReturnStmt) {
> 				Stmt gotoStmt = Jimple.v().newGotoStmt(endnop);
> 				units.swapWith(u, gotoStmt);
> 				for (Iterator trIt = b.getTraps().iterator(); trIt.hasNext();) {
> 					final Trap tr = (Trap) trIt.next();
> 					for (Iterator boxIt = tr.getUnitBoxes().iterator(); boxIt
> 							.hasNext();) {
> 						final UnitBox box = (UnitBox) boxIt.next();
> 						if (box.getUnit() == u)
> 							box.setUnit(gotoStmt);
> 					} // each box in trap
> 				} // each trap
> 			} // if return stmt
> 		} // each stmt in body
> 		//returns.put(b, endnop);
> 		return (endnop);
> 	}
> 
> 
>   /**
>    * inline a call to this() if one exists, return a ConstructorInliningMap if
>    * an inlining was done, null otherwise (no this() to inline).
>    */
>   public static ConstructorInliningMap inlineThisCall(SootMethod method){
>         // assure body is a constructor
>         if (!method.getName().equals("<init>"))
> 	   throw new InternalCompilerError 
> 	     ("trying to inline a this() in a method that is not an <init>");
> 
> 	// get the body
> 	Body b = method.getActiveBody();
> 
> 	// get the units
>         Chain containerUnits = b.getUnits();
> 
>         // if the first invoke is a this() and not a super() inline the this()
>         InvokeStmt invokeStmt = findInitStmt(containerUnits);
> 
>         SpecialInvokeExpr specInvokeExpr = 
> 	    (SpecialInvokeExpr)invokeStmt.getInvokeExpr();
> 
>         // if it is a this() call, need to do the inlining
>         if (specInvokeExpr.getMethodRef().declaringClass().equals(
> 	                         b.getMethod().getDeclaringClass())){
>             
>             // put locals from inlinee into container
>             SootMethod inlinee = specInvokeExpr.getMethodRef().resolve();
>             if (!inlinee.hasActiveBody()){
>                 inlinee.retrieveActiveBody();
>             }
> 
> 	    Body inlineeBody=inlinee.getActiveBody();
> 
>             HashMap oldLocalsToNew = new HashMap();
>             
>             Iterator localsIt = inlineeBody.getLocals().iterator();
>             while (localsIt.hasNext()){
>                 Local l = (Local)localsIt.next();
>                 Local newLocal = (Local)l.clone();
>                 b.getLocals().add(newLocal);
>                 oldLocalsToNew.put(l, newLocal);
>             }
>            
>             //find identity stmt of original method
>             IdentityStmt origIdStmt = findIdentityStmt(b);
>            
>             HashMap oldStmtsToNew = new HashMap();
>             
>             //System.out.println("locals: "+b.getLocals());
>             Iterator inlineeIt = inlineeBody.getUnits().iterator();
>             while (inlineeIt.hasNext()){
>                 Stmt inlineeStmt = (Stmt)inlineeIt.next();
>                
>                 // handle identity stmts
>                 if (inlineeStmt instanceof IdentityStmt){
>                     IdentityStmt idStmt = (IdentityStmt)inlineeStmt;
>                     
>                     if (idStmt.getRightOp() instanceof ThisRef) {
>                         Stmt newThis = Jimple.v().newAssignStmt((Local)oldLocalsToNew.get(idStmt.getLeftOp()), origIdStmt.getLeftOp());         
>                         newThis.addAllTagsOf(idStmt);
>                         containerUnits.insertBefore(newThis, invokeStmt);
>                         oldStmtsToNew.put(inlineeStmt, newThis);
>                     }
>                     
>                     else if (idStmt.getRightOp() instanceof CaughtExceptionRef){
>                         Stmt newInlinee = (Stmt)inlineeStmt.clone();
>                         Iterator localsToPatch = newInlinee.getUseAndDefBoxes().iterator();
>                         while (localsToPatch.hasNext()){
>                             ValueBox next = (ValueBox)localsToPatch.next();
>                             if (next.getValue() instanceof Local){
>                                 next.setValue((Local)oldLocalsToNew.get(next.getValue()));
>                             }
>                         }
>                        
>                         newInlinee.addAllTagsOf(inlineeStmt);
>                         containerUnits.insertBefore(newInlinee, invokeStmt);
>                         oldStmtsToNew.put(inlineeStmt, newInlinee);
>                     }
>                     else if (idStmt.getRightOp() instanceof ParameterRef) {
>                         Stmt newParam = Jimple.v().newAssignStmt((Local)oldLocalsToNew.get(idStmt.getLeftOp()), specInvokeExpr.getArg(((ParameterRef)idStmt.getRightOp()).getIndex()));         
>                         newParam.addAllTagsOf(idStmt);
>                         containerUnits.insertBefore(newParam, invokeStmt);
>                         oldStmtsToNew.put(inlineeStmt, newParam);
>                     }
>                 }
> 
>                 // handle return void stmts (cannot return anything else 
>                 // from a constructor)
>                 else if (inlineeStmt instanceof ReturnVoidStmt){
>                     Stmt newRet = Jimple.v().newGotoStmt((Stmt)containerUnits.getSuccOf(invokeStmt));
>                     newRet.addAllTagsOf(inlineeStmt);
>                     containerUnits.insertBefore(newRet, invokeStmt);
>                     debug("adding to stmt map: "+inlineeStmt+" and "+newRet);
>                     oldStmtsToNew.put(inlineeStmt, newRet);
>                 }
> 
>                 else {
>                     Stmt newInlinee = (Stmt)inlineeStmt.clone();
>                     Iterator localsToPatch = newInlinee.getUseAndDefBoxes().iterator();
>                     while (localsToPatch.hasNext()){
>                         ValueBox next = (ValueBox)localsToPatch.next();
>                         if (next.getValue() instanceof Local){
>                             next.setValue((Local)oldLocalsToNew.get(next.getValue()));
>                         }
>                     }
> 
>                     newInlinee.addAllTagsOf(inlineeStmt);   
>                     containerUnits.insertBefore(newInlinee, invokeStmt);
>                     oldStmtsToNew.put(inlineeStmt, newInlinee);
>                 }
>                 
>             }
>                 
>             // handleTraps
>             Iterator trapsIt = inlineeBody.getTraps().iterator();
>             while (trapsIt.hasNext()){
>                 Trap t = (Trap)trapsIt.next();
>                 debug("begin: "+t.getBeginUnit());
>                 Stmt newBegin = (Stmt)oldStmtsToNew.get(t.getBeginUnit());
>                 debug("end: "+t.getEndUnit());
>                 Stmt newEnd = (Stmt)oldStmtsToNew.get(t.getEndUnit());
>                 debug("handler: "+t.getHandlerUnit());
>                 Stmt newHandler = (Stmt)oldStmtsToNew.get(t.getHandlerUnit());
> 
>                 if (newBegin == null || newEnd == null || newHandler == null)
>                     throw new RuntimeException("couldn't map trap!");
> 
>                 b.getTraps().add(Jimple.v().newTrap(t.getException(), newBegin, newEnd, newHandler));
>             }
> 
>             // patch gotos
>             inlineeIt = inlineeBody.getUnits().iterator();
>             while (inlineeIt.hasNext()){
>                 Stmt newStmt = (Stmt) oldStmtsToNew.get(inlineeIt.next());
> 		Iterator unitBoxesIt=newStmt.getUnitBoxes().iterator();
> 		while(unitBoxesIt.hasNext()) {
> 		    UnitBox box=(UnitBox) unitBoxesIt.next();
> 		    if(oldStmtsToNew.containsKey(box.getUnit()))
> 			box.setUnit((Stmt) oldStmtsToNew.get(box.getUnit()));
> 		}
> 	    }
>                 
>             // remove original invoke
>             containerUnits.remove(invokeStmt);
>                
>             // resolve name collisions
>             LocalNameStandardizer.v().transform(b, "ji.lns");
> 
>             ConstructorInliningMap cim = new ConstructorInliningMap(inlinee, method);
>             cim.add(oldLocalsToNew);
>             cim.add(oldStmtsToNew);
> 
> 	    // return cim to indicate an inlining happened
> 	    return cim;
>         }
> 	return null; // no inlining
>     }
> 
>     private static IdentityStmt findIdentityStmt(Body b){
>         Iterator it = b.getUnits().iterator();
>         while (it.hasNext()){
>             Stmt s = (Stmt)it.next();
>             if ((s instanceof IdentityStmt) && (((IdentityStmt)s).getRightOp() instanceof ThisRef)){
>                 return (IdentityStmt)s;
>             }
>         }
>         return null;
>     }
> 
>     //private static Map/*<SootMethod,Local>*/ thiscopies=new Hashtable();
> 
>     /** Restructure the method to place a copy of 'this' in a new local
>      *  variable. Why is this useful? The local bound by the relevant 
>      *  identity statement will always point to local variable slot 0
>      *  (ignoring ITD stuff), which will always be 'this' at method entry
>      *  However, optimised bytecode might reuse slot 0 at a later point,
>      *  and we want to be sure we really have a handle on the value of
>      *  'this', not whatever it gets reused for later (which might even
>      *  be the wrong type).
>      * 
>      *  UPDATE: Experiments show that the this-local never changes throughout the method,
>      *  even if the byte-code assigns to slot 0. In this case, Jimple creates a new local
>      *  to assign to.
>      *  FIXME relational aspects will actually set the this-local to another value...
>      *  (Eric)
>      */
>     public static Local getThisLocal(SootMethod m) {
> 	//if(thiscopies.containsKey(m)) return ((Local) thiscopies.get(m));
> 	
> 	Body b=m.getActiveBody();
> 	if (MethodCategory.hasThisAsFirstParameter(m)) {
> 		return b.getParameterLocal(0);
> 	} else {
> 		return b.getThisLocal();
> 	}
> 	
> 	/*
> 	
> 	// don't want exceptions "fixed" up
> 	Chain units=b.getUnits().getNonPatchingChain();
> 	for(Stmt stmt=(Stmt) units.getFirst(); ;
> 	    stmt=(Stmt) units.getSuccOf(stmt)) {
> 	    
> 	    if(stmt==null) throw new RuntimeException
> 			       ("internal error: didn't find identitystmt binding this "
> 				+"in method "+m);
> 
> 	    if(stmt instanceof IdentityStmt) {
> 			IdentityStmt istmt=(IdentityStmt) stmt;
> 			
> 			// for ITDs "this" means the first parameter
> 			if (MethodCategory.hasThisAsFirstParameter(m)) {
> 				if (istmt.getRightOp() instanceof ParameterRef) {
> 					ParameterRef parref = (ParameterRef) istmt.getRightOp();
> 					if (parref.getIndex()==0) {
> 					   Value tr=istmt.getLeftOp();
> 					   do { 
> 					   stmt=(Stmt) units.getSuccOf(stmt); 
> 					   } while(stmt instanceof IdentityStmt);
> 					   units.insertBefore(Jimple.v().newAssignStmt(l,tr),stmt);
> 					   break;
> 					}
> 				}
> 			} else 
> 			// the normal non-ITD case
> 			if(istmt.getRightOp() instanceof ThisRef) {
> 			    Value tr=istmt.getLeftOp();
> 			    do { 
> 				stmt=(Stmt) units.getSuccOf(stmt); 
> 			    } while(stmt instanceof IdentityStmt);
> 			    units.insertBefore(Jimple.v().newAssignStmt(l,tr),stmt);
> 			    break;
> 			}
> 	    }
> 	}
> 	thiscopies.put(m,l);
> 	return l;*/
>     }
> 
>     private static Map/*<InvokeStmt,AssignStmt>*/ invokeassignstmts=new Hashtable();
>     
>     public static AssignStmt getEquivAssignStmt(SootMethod m,InvokeStmt stmt) {
> 	// We assume that a statement will only ever occur in one method
> 	// If this is not true, then use a separate table to check whether
> 	// the assignstmt has been repatched into a particular method
> 
> 	if(invokeassignstmts.containsKey(stmt)) 
> 	    return ((AssignStmt) (invokeassignstmts.get(stmt)));
> 
> 	Body b=m.getActiveBody();
> 	Chain units=b.getUnits();
> 	InvokeExpr e=stmt.getInvokeExpr();
> 	Local l=new LocalGeneratorEx(b).generateLocal(e.getMethodRef().returnType(),"retval");
> 	AssignStmt a=Jimple.v().newAssignStmt(l,e);
> 
> 	units.swapWith(stmt,a);
> 	if(stmt.hasTag("SourceLnPosTag")) a.addTag(stmt.getTag("SourceLnPosTag"));
> 	invokeassignstmts.put(stmt,a);
> 	return a;
>     }
> 	
> 	/**
> 	 * Sanity check for methods.
> 	 * Checks for:
> 	 * 	- @this identity statement as the first statement
> 	 *  - parameter identity statements in the right order for all parameters
> 	 * @param method
> 	 */
> 	public static void validateMethod(SootMethod method) {
> 		debug("validating " + method.getName());
> 		
> 		if (method.isAbstract())
> 			return;
> 		
> 		Body body=method.getActiveBody();
> 		Chain units=body.getUnits().getNonPatchingChain();
> 		List params=method.getParameterTypes();
> 		
> 		Iterator itUnits=units.iterator();
> 		if (!method.isStatic()) {
> 			Stmt first=(Stmt)itUnits.next();		
> 			
> 			IdentityStmt id=(IdentityStmt) first;
> 			Local local=(Local)id.getLeftOp();
> 			ThisRef ref=(ThisRef)id.getRightOp();
> 			if (!ref.getType().equals(method.getDeclaringClass().getType()))
> 				throw new RuntimeException();
> 			
> 			if (!local.getType().equals(method.getDeclaringClass().getType()))
> 				throw new RuntimeException();
> 			
> 		}	
> 		
> 		Iterator it=params.iterator();
> 		int i=0;
> 		while (it.hasNext()) {
> 			Type type=(Type)it.next();
> 			Stmt stmt=(Stmt)itUnits.next();
> 			IdentityStmt id=(IdentityStmt)stmt;
> 			Local local=(Local)id.getLeftOp();
> 			
> 			debug("  parameter " + i + ": " + type.toString() + ":" + local.getName());
> 			debug("   rightOp: " + id.getRightOp());
> 			ParameterRef ref=(ParameterRef)id.getRightOp();
> 		
> 					
> 			
> 			if (!Type.toMachineType(local.getType()).equals(Type.toMachineType(type))) {
> 				debug("type mismatch: local: " + local.getType() + " param: " + type);
> 				throw new RuntimeException();
> 			}
> 			if (ref.getIndex()!=i++) {
> 				throw new RuntimeException();
> 			}
> 			if (!ref.getType().equals(type)) {
> 				throw new RuntimeException();
> 			}				
> 		}
> 		
> 	}
> 
> 	/**
> 	 * This class helps implement boxing.
> 	 * It assigns integer IDs to the simple Java types and
> 	 * one ID to all the reference types.
> 	 * 
> 	 * It contains methods to retrieve the boxing classes of simple types
> 	 * and their value methods (intVal() etc.) 
> 	 * 
> 	 * @author Sascha Kuzins 
> 	 */
> 	public static class JavaTypeInfo {
> 		public final static int booleanType=0;
> 		public final static int byteType=1;
> 		public final static int shortType=2;
> 		public final static int charType=3;
> 		public final static int intType=4;
> 		public final static int longType=5;
> 		public final static int floatType=6;
> 		public final static int doubleType=7;
> 		public final static int refType=8;
> 		public final static int typeCount=9;
> 		
> 		/*
> 		 * This table is from Java in a Nutshell 3rd edition, page 28
> 		 * X - same type
> 		 * N - forbidden conversion
> 		 * Y - widening conversion
> 		 * C - narrowing conversion
> 		 */
> 		private final static char [][] simple_conversions =
> 			{ 
> 				{ 'X', 'N', 'N', 'N', 'N', 'N', 'N', 'N'},
> 				{ 'N', 'X', 'Y', 'C', 'Y', 'Y', 'Y', 'Y'},
> 				{ 'N', 'C', 'X', 'C', 'Y', 'Y', 'Y', 'Y'},
> 				{ 'N', 'C', 'C', 'X', 'Y', 'Y', 'Y', 'Y'},
> 				{ 'N', 'C', 'C', 'C', 'X', 'Y', 'Y', 'Y'},
> 				{ 'N', 'C', 'C', 'C', 'C', 'X', 'Y', 'Y'},
> 				{ 'N', 'C', 'C', 'C', 'C', 'C', 'X', 'Y'},
> 				{ 'N', 'C', 'C', 'C', 'C', 'C', 'C', 'X'}
> 			};
> 		public static char getSimpleTypeConversionInfo(Type from, Type to) {
> 			int f=sootTypeToInt(from);
> 			if (!isSimpleType(f))
> 				throw new RuntimeException();
> 			int t=sootTypeToInt(to);
> 			if (!isSimpleType(t))
> 				throw new RuntimeException();
> 			
> 			//if (simple_conversions[longType][intType]!='C')
> 			//	throw new RuntimeException();
> 			
> 			return simple_conversions[f][t];
> 		}
> 		
> 		public static boolean isSimpleType(Type t) {
> 			return isSimpleType(sootTypeToInt(t));
> 		}
> 		
> 		public static boolean isSimpleType(int type) {
> 			return type!=refType;
> 		}
> 		public static boolean isSimpleWideningConversion(Type from, Type to) {
> 			char c=getSimpleTypeConversionInfo(from, to);
> 			return c=='Y' || c=='X'; 
> 		}
> 		// returns if the classes contain a method with the same name and signature 
> 		// but different return type
> 		public static boolean haveCollidingMethod(SootClass cl, SootClass cl2) {
> 			for( Iterator mIt = cl.getMethods().iterator(); mIt.hasNext(); ) {
> 			    final SootMethod m = (SootMethod) mIt.next();
> 				try {
> 					
> 					SootMethod m2=cl2.getMethod(m.getName(), m.getParameterTypes());
> 					if (!m2.getReturnType().equals(m.getReturnType()))
> 						return true;
> 					
> 				} catch (RuntimeException e) {
> 					if (e.getMessage().matches("ambiguous method"))
> 						throw new RuntimeException("Internal error, unexpected exception.");
> 				}
> 			}
> 			return false;
> 		}
> 		
> 		public static boolean implementsInterfaceRecursive(SootClass cl, String interfaceName)  {
> 			if (cl.implementsInterface(interfaceName))
> 				return true;
> 			else {
> 				if (cl.hasSuperclass())
> 					return implementsInterfaceRecursive(cl.getSuperclass(), interfaceName);
> 				else
> 					return false;
> 			}
> 		}
> 		/*
> 		 * The aim is to return true for cases which javac would 
> 		 * identify as an invalid cast at compile time.
> 		 * (For example boolean to int, or unrelated RefTypes).
> 		 * Based on 5.1.7 of the Java Language Spec, second edition, page 64.
> 		 * Also see 5.5, Casting Conversion. 
> 		 * The #numbers in the code indicate the bullet point to which the test 
> 		 * refers.
> 		 */
> 		public static boolean isForbiddenConversion(Type from, Type to) {
> 			// AnySubType is only used for points-to analysis
> 			if (from instanceof AnySubType) throw new RuntimeException();
> 			if (to instanceof AnySubType) throw new RuntimeException();
> 			
> 			// #4
> 			if (to instanceof NullType && !(from instanceof NullType ))
> 				return true;			
> 			
> 			if (to.equals(from))
> 				return false;
> 			
> 			if (from instanceof NullType && to instanceof RefLikeType)
> 				return false;	
> 			
> 			// #3
> 			if (from instanceof NullType && isSimpleType(to))
> 				return true;
> 			
> 			if (isSimpleType(from) && isSimpleType(to))
> 				return isForbiddenSimpleConversion(from, to); // #5,6
> 			
> 			
> 			// At this stage, they are not both simple types.			
> 			// #1, #2
> 			if (isSimpleType(from) || isSimpleType(to))
> 				return true;
> 			
> 			if (!(from instanceof RefType && to instanceof RefType)) {
> 				// not both are reftypes
> 				
> 				// #12,13
> 				if (from instanceof ArrayType && to instanceof RefType &&
> 					! (		to.equals(RefType.v("java.lang.Object")) || 
> 							to.equals(RefType.v("java.io.Serializable")) ||
> 							to.equals(RefType.v("java.lang.Cloneable")) )) 
> 					return true; // cast from array to non-object reftype
> 				
> 				// #9
> 				if (to instanceof ArrayType && from instanceof RefType &&
> 						!from.equals(RefType.v("java.lang.Object"))) 
> 					return true; // cast from non-object reftype to array
> 				
> 				
> 				// #14
> 				if (to instanceof ArrayType && from instanceof ArrayType) {
> 				 	ArrayType ato=(ArrayType)to;
> 				 	ArrayType afrom=(ArrayType)from;
> 				 	return 
> 						isForbiddenConversion(afrom.baseType, ato.baseType);				 		 
> 				}				
> 				
> 				return false;
> 			}
> 			
> 			RefType rFrom=(RefType)from;
> 			RefType rTo=(RefType)to;
> 			
> 			// #11
> 			if (rFrom.getSootClass().isInterface() && rTo.getSootClass().isInterface()) {
> 				if (haveCollidingMethod(rFrom.getSootClass(), rTo.getSootClass()))
> 					return true;
> 				else
> 					return false;
> 			}
> 			// #8
> 			if (rTo.getSootClass().isInterface())  {
> 				if (Modifier.isFinal(rFrom.getSootClass().getModifiers()) &&
> 					implementsInterfaceRecursive(rFrom.getSootClass(), rTo.getSootClass().getName()))
> 					return true;
> 				else
> 					return false; // from could implement this interface
> 			}
> 			// #10
> 			if (rFrom.getSootClass().isInterface()) {  
> 				if (Modifier.isFinal(rTo.getSootClass().getModifiers()) &&
> 						implementsInterfaceRecursive(rTo.getSootClass(), rFrom.getSootClass().getName()))
> 					return true;
> 				else						// from can implement other interfaces and
> 					return false;			// a non-interface type, so cast could be possible
> 			}
> 			
> 			
> 			
> 			FastHierarchy hier=Scene.v().getOrMakeFastHierarchy();
> 			// #7
> 			if (hier.isSubclass(rFrom.getSootClass(), rTo.getSootClass()) ||
> 				hier.isSubclass(rTo.getSootClass(), rFrom.getSootClass())) 
> 				return false;
> 			else
> 				return true;
> 		}
> 		public static boolean isForbiddenSimpleConversion(Type from, Type to) {
> 			char c=getSimpleTypeConversionInfo(from, to);
> 			return c=='N';
> 		}
> 		
> 		public static int sootTypeToInt(Type type) {
> 			if (type.equals(IntType.v()))
> 				return intType;
> 			else if (type.equals(BooleanType.v())) 
> 				return booleanType;
> 			else if (type.equals(ByteType.v())) 
> 				return byteType;
> 			else if (type.equals(ShortType.v())) 
> 							return shortType;
> 			else if (type.equals(CharType.v())) 
> 							return charType;
> 			else if (type.equals(LongType.v())) 
> 							return longType;
> 			else if (type.equals(FloatType.v())) 
> 							return floatType;
> 			else if (type.equals(DoubleType.v())) 
> 							return doubleType;
> 			else 
> 				return refType;
> 		}
> 		public static Value getDefaultValue(Type type) {
> 			if (type.equals(IntType.v()))
> 				return IntConstant.v(0);
> 			else if (type.equals(BooleanType.v())) 
> 				return IntConstant.v(0); 
> 			else if (type.equals(ByteType.v())) 
> 				return IntConstant.v(0); ///
> 			else if (type.equals(ShortType.v())) 
> 				return IntConstant.v(0); ///
> 			else if (type.equals(CharType.v())) 
> 				return IntConstant.v(0); ///
> 			else if (type.equals(LongType.v())) 
> 				return LongConstant.v(0);
> 			else if (type.equals(FloatType.v())) 
> 				return FloatConstant.v(0.0f);
> 			else if (type.equals(DoubleType.v())) 
> 				return DoubleConstant.v(0.0);
> 			else 
> 				return NullConstant.v();
> 		}
> 		public static SootClass getBoxingClass(Type type) {
> 			if (type.equals(IntType.v()))
> 				return Scene.v().getSootClass("java.lang.Integer");
> 			else if (type.equals(BooleanType.v())) 
> 				return Scene.v().getSootClass("java.lang.Boolean");
> 			else if (type.equals(ByteType.v())) 
> 				return Scene.v().getSootClass("java.lang.Byte");
> 			else if (type.equals(ShortType.v())) 
> 				return Scene.v().getSootClass("java.lang.Short");
> 			else if (type.equals(CharType.v())) 
> 				return Scene.v().getSootClass("java.lang.Character");
> 			else if (type.equals(LongType.v())) 
> 				return Scene.v().getSootClass("java.lang.Long");
> 			else if (type.equals(FloatType.v())) 
> 				return Scene.v().getSootClass("java.lang.Float");
> 			else if (type.equals(DoubleType.v())) 
> 				return Scene.v().getSootClass("java.lang.Double");
> 			else 
> 				throw new RuntimeException();
> 		}
> 
> 		public static String getBoxingClassMethodName(Type type) {
> 			return getSimpleTypeBoxingClassMethodName(
> 					getBoxingClassPrimType(((RefType)type).getSootClass()));
> 		}
> 		public static String getSimpleTypeBoxingClassMethodName(Type type) {	
> 			if (type.equals(IntType.v()))
> 				return "intValue";
> 			else if (type.equals(BooleanType.v())) 
> 				return "booleanValue";
> 			else if (type.equals(ByteType.v())) 
> 				return "byteValue";
> 			else if (type.equals(ShortType.v())) 
> 				return "shortValue";
> 			else if (type.equals(CharType.v())) 
> 				return "charValue";
> 			else if (type.equals(LongType.v())) 
> 				return "longValue";
> 			else if (type.equals(FloatType.v())) 
> 				return "floatValue";
> 			else if (type.equals(DoubleType.v())) 
> 				return "doubleValue";
> 			else 
> 				throw new RuntimeException("no method for type " + type);
> 		}
> 		
> 		/** Given the boxing class, what was the primitive type?
> 		 * 
> 		 * @param boxingClass the boxing class (eg Integer)
> 		 * @return the primitive type (eg int)
> 		 */
> 		public static Type getBoxingClassPrimType(SootClass boxingClass) {
> 			if (boxingClass.equals(getBoxingClass(IntType.v())))
> 				return IntType.v();
> 			else if (boxingClass.equals(getBoxingClass(BooleanType.v()))) 
> 				return BooleanType.v();
> 			else if (boxingClass.equals(getBoxingClass(ByteType.v()))) 
> 				return ByteType.v();
> 			else if (boxingClass.equals(getBoxingClass(ShortType.v()))) 
> 				return ShortType.v();
> 			else if (boxingClass.equals(getBoxingClass(CharType.v()))) 
> 				return CharType.v();
> 			else if (boxingClass.equals(getBoxingClass(LongType.v()))) 
> 				return LongType.v();
> 			else if (boxingClass.equals(getBoxingClass(FloatType.v())))
> 				return FloatType.v();
> 			else if (boxingClass.equals(getBoxingClass(DoubleType.v()))) 
> 				return DoubleType.v();
> 			else 
> 				throw new RuntimeException();
> 		}
> 		public static boolean isBoxingType(Type type) {
> 			if (type instanceof RefType) {
> 				RefType rt=(RefType)type;
> 				try {
> 					getBoxingClassPrimType(rt.getSootClass());
> 					return true;
> 				} catch(RuntimeException e) {
> 					return false;
> 				}
> 			} else
> 				return false;
> 		}
> 	}
> 	/**
> 	 * Converts the assignment statement into a sequence 
> 	 * of statements performing a typecast.
> 	 * Boxes/unboxes if the assignment is from/to an Object to/from a simple type. 
> 	 * @param body
> 	 * @param stmt
> 	 */
> 	public static boolean insertBoxingCast(Body body, AssignStmt stmt, boolean allowBoxing) {
> 		boolean bDidUnBox=false;
> 		ValueBox source=stmt.getRightOpBox();
> 		Value targetVal=stmt.getLeftOp();
> 		Type targetType=stmt.getLeftOp().getType();
> 		Chain units=body.getUnits().getNonPatchingChain();
> 		Type sourceType=source.getValue().getType();
> 		if (!sourceType.equals(targetType) && 
> 			  !(sourceType instanceof RefType &&
> 				targetType instanceof RefType &&
> 				isBaseClass(((RefType)targetType).getSootClass(), 
> 							((RefType)sourceType).getSootClass()))) {
> 				
> 			
> 			
> 			LocalGeneratorEx localgen=new LocalGeneratorEx(body);
> 			Local castLocal=localgen.generateLocal(sourceType, "castTmp");
> 			debug("cast: source has type " + sourceType.toString());
> 			debug("cast: target has type " + targetType.toString());
> 			stmt.setLeftOp(castLocal);
> 			
> 			AssignStmt tmpStmt=Jimple.v().newAssignStmt(targetVal, targetVal /*dummy*/);
> 			units.insertAfter(tmpStmt, stmt);
> 						
> 			Value castedExpr;
> 			//debug("boxing: source " + sourceType + " target " + targetType);
> 			// boxing
> 			if (allowBoxing && JavaTypeInfo.sootTypeToInt(sourceType)!=JavaTypeInfo.refType &&
> 				targetType.equals(Scene.v().getSootClass("java.lang.Object").getType())) {
> 				SootClass boxClass=JavaTypeInfo.getBoxingClass(sourceType);	
> 				 Local box=localgen.generateLocal(boxClass.getType(), "box");
> 				 Stmt newAssignStmt = Jimple.v().newAssignStmt( box, Jimple.v().newNewExpr( boxClass.getType() ) );
> 				 List initParams=new LinkedList();
> 				 initParams.add(sourceType);
> 				 Stmt initBox=Jimple.v().newInvokeStmt( 
> 				 	Jimple.v().newSpecialInvokeExpr( box, Scene.v().makeConstructorRef(boxClass,initParams), 
> 				 			castLocal)) ;
> 				units.insertBefore(newAssignStmt, tmpStmt);
> 				units.insertBefore(initBox, tmpStmt);
> 				castedExpr=box;
> 				bDidUnBox=true;
> 			} else if /*unboxing*/
> 				(allowBoxing && JavaTypeInfo.sootTypeToInt(targetType)!=JavaTypeInfo.refType &&
> 					sourceType.equals(Scene.v().getSootClass("java.lang.Object").getType())	){ 
> 				SootClass boxClass=JavaTypeInfo.getBoxingClass(targetType);	
> 				Local box=localgen.generateLocal(boxClass.getType(), "box");
> 				Stmt newAssignStmt=Jimple.v().newAssignStmt(box, 
> 					Jimple.v().newCastExpr(castLocal, boxClass.getType()));
> 				SootMethodRef method=Scene.v().makeMethodRef
> 				    (boxClass,
> 				     JavaTypeInfo.getSimpleTypeBoxingClassMethodName(targetType),
> 				     new ArrayList(),
> 				     targetType,
> 				     false);
> 				castedExpr=Jimple.v().newVirtualInvokeExpr(box, 
> 						 method);		
> 				units.insertBefore(newAssignStmt, tmpStmt);	
> 				bDidUnBox=true;
> 			} else { // normal cast
> 				CastExpr castExpr=Jimple.v().newCastExpr(castLocal,targetType);
> 				castedExpr=castExpr;	
> 			}
> 			
> 			tmpStmt.setRightOp(castedExpr);
> 		//	Jimple.v().newCastExpr()
> 			/*
> 			if (stmt instanceof AssignStmt) {
> 				source.setValue(castedExpr);
> 			} else {
> 				Local tmpLocal=localgen.generateLocal(targetType, "castTarget");
> 				AssignStmt tmpStmt2=Jimple.v().newAssignStmt(tmpLocal, castedExpr);
> 				units.insertBefore(tmpStmt2, stmt);
> 				source.setValue(tmpLocal);
> 			}*/
> 		} 		
> 		return bDidUnBox;
> 	}
> 	/**
> 	 * Retrieves the identity statement of the argument at position arg
> 	 * @param method
> 	 * @param arg
> 	 * @return
> 	 */
> 	public static IdentityStmt getParameterIdentityStatement(SootMethod method, int arg) {
> 		if (arg>=method.getParameterCount())
> 			throw new RuntimeException();
> 		Chain units=method.getActiveBody().getUnits().getNonPatchingChain();
> 		Iterator it=units.iterator();
> 		while (it.hasNext()) {
> 			Stmt stmt=(Stmt)it.next();
> 			if (stmt instanceof IdentityStmt) {
> 				IdentityStmt ids=(IdentityStmt)stmt;
> 				if (ids.getRightOp() instanceof ParameterRef) {
> 					ParameterRef paramRef=(ParameterRef)ids.getRightOp();
> 					if (paramRef.getIndex()==arg)
> 						return ids;
> 					
> 				} else if (ids.getRightOp() instanceof ThisRef) {
> 					
> 				} else 
> 					throw new RuntimeException();
> 			} else
> 				throw new RuntimeException();
> 		}
> 		throw new RuntimeException();
> 	}
> 	/**
> 	 * Adds a new parameter to a method and creates the matching identity statement.
> 	 * Returns the local of the newly created parameter.
> 	 * @param method
> 	 * @param type
> 	 * @param suggestedName
> 	 * @return
> 	 */
> 	public static Local addParameterToMethod(SootMethod method, Type type, String suggestedName) {
> 		//validateMethod(method);
> 		Body body=method.getActiveBody();
> 		Chain units=body.getUnits().getNonPatchingChain();
> 		List params=new LinkedList(method.getParameterTypes());
> 		
> 		IdentityStmt lastIDStmt=null;
> 		if (params.isEmpty()) {
> 			if (units.isEmpty()) {
> 				if (!method.isStatic())
> 					throw new RuntimeException();
> 			} else {
> 				lastIDStmt=(IdentityStmt)units.getFirst();
> 				if (! (lastIDStmt.getRightOp() instanceof ThisRef))
> 					if (!method.isStatic())
> 						throw new RuntimeException();
> 			}
> 		} else {
> 		//	debug("param id: " + (params.size()-1));
> 			lastIDStmt=Restructure.getParameterIdentityStatement(method, params.size()-1);
> 		}
> 		params.add(type);
> 		method.setParameterTypes(params);
> 		LocalGeneratorEx lg=new LocalGeneratorEx(body);
> 		Local l=lg.generateLocal(type, suggestedName);
> 		IdentityStmt newIDStmt=Jimple.v().newIdentityStmt(l, 
> 			Jimple.v().newParameterRef(type, params.size()-1));
> 		if (lastIDStmt==null)
> 			units.addFirst(newIDStmt);
> 		else
> 			units.insertAfter(newIDStmt, lastIDStmt);
> 		return l;		
> 	}
> 
> 	public static boolean isBaseClass(SootClass baseClass, SootClass subClass) {
> 		SootClass sub = subClass;
> 	
> 		while (sub.hasSuperclass()) {
> 			SootClass superClass = sub.getSuperclass();
> 			if (superClass.equals(baseClass))
> 				return true;
> 	
> 			sub = superClass;
> 		}
> 		return false;
> 	}	
> 	
> 
> } // class Restructure
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Soot-list mailing list
> Soot-list at sable.mcgill.ca
> http://mailman.cs.mcgill.ca/mailman/listinfo/soot-list


More information about the Soot-list mailing list