/* Soot - a J*va Optimization Framework * Copyright (C) 2006 Nomair A. Naeem * * 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. */ package soot.dava.toolkits.base.renamer; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import soot.ArrayType; import soot.Local; import soot.RefLikeType; import soot.SootClass; import soot.SootField; import soot.Type; import soot.dava.internal.AST.ASTMethodNode; import soot.util.Chain; public class Renamer { public final boolean DEBUG = false; heuristicSet heuristics; List locals; // a list of locals in scope Chain fields; // a list of fields in scope ASTMethodNode methodNode; List forLoopNames; HashMap<Local, Boolean> changedOrNot;//keeps track of which local was changed previously public Renamer(heuristicSet info, ASTMethodNode node) { heuristics = info; locals = null; methodNode = node; changedOrNot = new HashMap<Local, Boolean>(); Iterator<Local> localIt = info.getLocalsIterator(); while(localIt.hasNext()) changedOrNot.put(localIt.next(),new Boolean(false)); forLoopNames = new ArrayList(); forLoopNames.add("i"); forLoopNames.add("j"); forLoopNames.add("k"); forLoopNames.add("l"); } /* * Add any naming heuristic as a separate method and invoke the method from * this method. * * HOWEVER, NOTE that the order of naming really really matters */ public void rename() { debug("rename","Renaming started"); // String args mainMethodArgument(); //for(i=0;i<bla;i++) forLoopIndexing(); //exceptions are named using first letter of each capital char in the class name exceptionNaming(); //arrays get <type>Array arraysGetTypeArray(); //if a local is assigned a field that name can be used since fields are conserved assignedFromAField(); //check if a local is assigned the result of a new invocation newClassName(); //check if a local is assigned after casting castedObject(); //if nothing else give a reference the name of the class objectsGetClassName(); //atleast remove the ugly dollar signs removeDollarSigns(); } /* * if there is an array int[] x. then if no other heuristic matches give it the name intArray */ private void arraysGetTypeArray(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if(alreadyChanged(tempLocal)){ continue; } debug("arraysGetTypeArray","checking "+tempLocal); Type type = tempLocal.getType(); if(type instanceof ArrayType){ debug("arraysGetTypeArray","Local:"+tempLocal+" is an Array Type: "+type.toString()); String tempClassName = type.toString(); //remember that a toString of an array gives you the square brackets if(tempClassName.indexOf('[')>=0) tempClassName = tempClassName.substring(0,tempClassName.indexOf('[')); //debug("arraysGetTypeArray","type of object is"+tempClassName); if(tempClassName.indexOf('.')!= -1){ //contains a dot have to remove that tempClassName=tempClassName.substring(tempClassName.lastIndexOf('.')+1); } String newName = tempClassName.toLowerCase(); newName = newName+"Array"; int count=0; newName += count; count++; while(!isUniqueName(newName)){ newName = newName.substring(0,newName.length()-1)+count; count++; } setName(tempLocal,newName); } } } /* * The method assigns any local whose name hasnt been changed yet to * the name of the class type it belongs to */ private void objectsGetClassName(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if(alreadyChanged(tempLocal)){ continue; } debug("objectsGetClassName","checking "+tempLocal); Type type = tempLocal.getType(); if(type instanceof ArrayType){ //should have been handled by arraysGetTypeArray heuristic continue; } if(type instanceof RefLikeType){ debug("objectsGetClassName","Local:"+tempLocal+" Type: "+type.toString()); //debug("objectsGetClassName","getting array type"+type.getArrayType()); String tempClassName = type.toString(); //debug("objectsGetClassName","type of object is"+tempClassName); if(tempClassName.indexOf('.')!= -1){ //contains a dot have to remove that tempClassName=tempClassName.substring(tempClassName.lastIndexOf('.')+1); } String newName = tempClassName.toLowerCase(); int count=0; newName += count; count++; while(!isUniqueName(newName)){ newName = newName.substring(0,newName.length()-1)+count; count++; } setName(tempLocal,newName); } } } /* * If a local is assigned the resullt of a cast expression temp = (List) object; * then u can use list as the name...however only if its always casted to the same object */ private void castedObject(){ debug("castedObject",""); Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if(!alreadyChanged(tempLocal)){ debug("castedObject","checking "+tempLocal); List<String> classes = heuristics.getCastStrings(tempLocal); Iterator<String> itClass = classes.iterator(); String classNameToUse = null; while(itClass.hasNext()){ String tempClassName = itClass.next(); if(tempClassName.indexOf('.')!= -1){ //contains a dot have to remove that tempClassName=tempClassName.substring(tempClassName.lastIndexOf('.')+1); } if(classNameToUse == null) classNameToUse = tempClassName; else if(!classNameToUse.equals(tempClassName)){ //different new assignment //cant use these classNames classNameToUse=null; break; } }//going through class names stored if(classNameToUse!=null){ debug("castedObject","found a classNametoUse through cast expr"); /* * We should use this classNAme to assign to the local name * We are guaranteed that all cast expressions use this type */ String newName = classNameToUse.toLowerCase(); int count=0; newName += count; count++; while(!isUniqueName(newName)){ newName = newName.substring(0,newName.length()-1)+count; count++; } setName(tempLocal,newName); } }//not already changed }//going through locals } /* * See if any local was initialized using the new operator * That name might give us a hint to a name to use for the local */ private void newClassName(){ debug("newClassName",""); //check if CLASSNAME is set //that would mean there was new className invocation Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if(!alreadyChanged(tempLocal)){ debug("newClassName","checking "+tempLocal); List<String> classes = heuristics.getObjectClassName(tempLocal); Iterator<String> itClass = classes.iterator(); String classNameToUse = null; while(itClass.hasNext()){ String tempClassName = itClass.next(); if(tempClassName.indexOf('.')!= -1){ //contains a dot have to remove that tempClassName=tempClassName.substring(tempClassName.lastIndexOf('.')+1); } if(classNameToUse == null) classNameToUse = tempClassName; else if(!classNameToUse.equals(tempClassName)){ //different new assignment //cant use these classNames classNameToUse=null; break; } }//going through class names stored if(classNameToUse!=null){ debug("newClassName","found a classNametoUse"); /* * We should use this classNAme to assign to the local name * We are guaranteed that all new invocations use this class name */ String newName = classNameToUse.toLowerCase(); int count=0; newName += count; count++; while(!isUniqueName(newName)){ newName = newName.substring(0,newName.length()-1)+count; count++; } setName(tempLocal,newName); } }//not already changed }//going through locals } /* * If a local is assigned from a field (static or non staitc) we can use that name * to assign a some what better name for the local * * If multiple fields are assigned then it might be a better idea to not do * anything since that will only confuse the user * */ private void assignedFromAField(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if(!alreadyChanged(tempLocal)){ debug("assignedFromField","checking "+tempLocal); List<String> fieldNames = heuristics.getFieldName(tempLocal); if(fieldNames.size()>1){ //more than one fields were assigned to this var continue; } else if(fieldNames.size()==1){ //only one field was used String fieldName = fieldNames.get(0); //okkay to use the name of the field if its not in scope //eg it was some other classes field int count=0; while(!isUniqueName(fieldName)){ if(count==0) fieldName = fieldName+count; else fieldName = fieldName.substring(0,fieldName.length()-1)+count; count++; } setName(tempLocal,fieldName); }//only one field assigned to this local }//not changed }//going through locals } /* * If we cant come up with any better name atleast we should remove the $ signs */ private void removeDollarSigns(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); String currentName = tempLocal.getName(); int dollarIndex = currentName.indexOf('$'); if(dollarIndex == 0){ //meaning there is a $ sign in the first location String newName = currentName.substring(1,currentName.length()); if(isUniqueName(newName)){ setName(tempLocal,newName); // System.out.println("Changed "+currentName+" to "+newName); //tempLocal.setName(newName); } } } } /* * */ private void exceptionNaming(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); Type localType = tempLocal.getType(); String typeString = localType.toString(); if(typeString.indexOf("Exception")>=0){ //the string xception occurs in this type debug("exceptionNaming","Type is an exception"+ tempLocal); //make a new name of all caps characters in typeString String newName = ""; for(int i=0;i<typeString.length();i++){ char character = typeString.charAt(i); if(Character.isUpperCase(character)){ newName += Character.toLowerCase(character); } } int count =0; if(!isUniqueName(newName)){ count++; while(!isUniqueName(newName+count)){ count++; } } if(count !=0) newName = newName + count; setName(tempLocal,newName); } } } /* * Probably one of the most common programming idioms * for loop indexes are often i j k l */ private void forLoopIndexing(){ Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); debug("foeLoopIndexing","Checking local"+tempLocal.getName()); if (heuristics.getHeuristic(tempLocal, infoGatheringAnalysis.FORLOOPUPDATE)) { // this local variable is the main argument // will like to set it to args if no one has an objection int count = -1; String newName; do{ count++; if(count>=forLoopNames.size()){ newName=null; break; } newName = (String)forLoopNames.get(count); }while (!isUniqueName(newName)); if(newName!=null){ setName(tempLocal,newName); } } } } /* * A simple heuristic which sets the mainMethodArgument's name to args */ private void mainMethodArgument() { Iterator<Local> it = heuristics.getLocalsIterator(); while (it.hasNext()) { Local tempLocal = it.next(); if (heuristics.getHeuristic(tempLocal, infoGatheringAnalysis.MAINARG)) { // this local variable is the main argument // will like to set it to args if no one has an objection String newName = "args"; int count = 0; while (!isUniqueName(newName)) { if(count==0) newName = newName+count; else newName = newName.substring(0,newName.length()-1)+count; count++; } setName(tempLocal,newName); //there cant be a same local with this heuristic set so just return return; } } } /* * In order to make sure that some previous heuristic which is usually a STRONGER * heuristic has not already changed the name we use this method which checks for * past name changes and only changes the name if the name hasnt been changed previously */ private void setName(Local var, String newName){ Object truthValue = changedOrNot.get(var); //if it wasnt in there add it if(truthValue == null) changedOrNot.put(var,new Boolean(false)); else{ if(((Boolean)truthValue).booleanValue()){ //already changed just return debug("setName","Var: "+var + " had already been renamed"); return; } } //will only get here if the var had not been changed debug("setName","Changed "+var.getName()+" to "+newName); var.setName(newName); changedOrNot.put(var,new Boolean(true)); } /* * Check if a local has already been changed * @param local to check * @return true if already changed otherwise false */ private boolean alreadyChanged(Local var){ Object truthValue = changedOrNot.get(var); //if it wasnt in there add it if(truthValue == null){ changedOrNot.put(var,new Boolean(false)); return false; } else{ if(((Boolean)truthValue).booleanValue()){ //already changed just return debug("alreadyChanged","Var: "+var + " had already been renamed"); return true; } else return false; } } /* * Should return true if the name is unique */ private boolean isUniqueName(String name) { Iterator it = getScopedLocals(); // check that none of the locals uses this name while (it.hasNext()) { Local tempLocal = (Local) it.next(); if (tempLocal.getName().equals(name)){ debug("isUniqueName","New Name "+ name+ " is not unique (matches some local)..changing"); return false; } else debug("isUniqueName","New Name "+ name+ " is different from local "+ tempLocal.getName()); } it = getScopedFields(); // check that none of the fields uses this name while (it.hasNext()) { SootField tempField = (SootField) it.next(); if (tempField.getName().equals(name)){ debug("isUniqueName","New Name "+ name+ " is not unique (matches field)..changing"); return false; } else debug("isUniqurName","New Name "+ name+ " is different from field "+ tempField.getName()); } return true; } /* * Method is responsible to find all names with which there could be a * potential clash The variables are: all the fields of this class and all * the locals defined in this method */ private Iterator getScopedFields() { // get the fields for this class and store them SootClass sootClass = methodNode.getDavaBody().getMethod() .getDeclaringClass(); fields = sootClass.getFields(); return fields.iterator(); } /* * Method is responsible to find all variable names with which there could * be a potential clash The variables are: all the fields of this class and * all the locals defined in this method */ private Iterator getScopedLocals() { Iterator<Local> it = heuristics.getLocalsIterator(); locals = new ArrayList(); while(it.hasNext()) locals.add(it.next()); return locals.iterator(); } public void debug(String methodName, String debug){ if(DEBUG) System.out.println(methodName+ " DEBUG: "+debug); } }