/* Soot - a J*va Optimization Framework * Copyright (C) 2004 Jennifer Lhotak * * 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.jimple.toolkits.annotation.qualifiers; import soot.*; import java.util.*; import soot.tagkit.*; import soot.jimple.*; import soot.jimple.toolkits.callgraph.*; /** a scene transformer that add tags to indicate the tightest qualifies * possible for fields and methods (ie: private, protected or public) */ public class TightestQualifiersTagger extends SceneTransformer { public TightestQualifiersTagger(Singletons.Global g) {} public static TightestQualifiersTagger v() { return G.v().soot_jimple_toolkits_annotation_qualifiers_TightestQualifiersTagger();} public final static int RESULT_PUBLIC = 0; public final static int RESULT_PACKAGE = 1; public final static int RESULT_PROTECTED = 2; public final static int RESULT_PRIVATE = 3; private final HashMap<SootMethod, Integer> methodResultsMap = new HashMap<SootMethod, Integer>(); private final HashMap<SootField, Integer> fieldResultsMap = new HashMap<SootField, Integer>(); private MethodToContexts methodToContexts; protected void internalTransform(String phaseName, Map options){ handleMethods(); handleFields(); } private void handleMethods() { Iterator classesIt = Scene.v().getApplicationClasses().iterator(); while (classesIt.hasNext()){ SootClass appClass = (SootClass)classesIt.next(); Iterator methsIt = appClass.getMethods().iterator(); while (methsIt.hasNext()){ SootMethod sm = (SootMethod)methsIt.next(); // for now if its unreachable do nothing if (!Scene.v().getReachableMethods().contains(sm)) continue; analyzeMethod(sm); } } Iterator<SootMethod> methStatIt = methodResultsMap.keySet().iterator(); while (methStatIt.hasNext()) { SootMethod meth = methStatIt.next(); int result = methodResultsMap.get(meth).intValue(); String sRes = "Public"; if (result == RESULT_PUBLIC){ sRes = "Public"; } else if (result == RESULT_PROTECTED){ sRes = "Protected"; } else if (result == RESULT_PACKAGE){ sRes = "Package"; } else if (result == RESULT_PRIVATE){ sRes = "Private"; } String actual = null; if (Modifier.isPublic(meth.getModifiers())){ actual = "Public"; } else if (Modifier.isProtected(meth.getModifiers())){ actual = "Protected"; } else if (Modifier.isPrivate(meth.getModifiers())){ actual = "Private"; } else { actual = "Package"; } //System.out.println("Method: "+meth.getName()+" has "+actual+" level access, can have: "+sRes+" level access."); if (!sRes.equals(actual)) { if (meth.getName().equals("<init>")){ meth.addTag(new StringTag("Constructor: "+meth.getDeclaringClass().getName()+" has "+actual+" level access, can have: "+sRes+" level access.", "Tightest Qualifiers")); } else { meth.addTag(new StringTag("Method: "+meth.getName()+" has "+actual+" level access, can have: "+sRes+" level access.", "Tightest Qualifiers")); } meth.addTag(new ColorTag(255, 10, 0, true, "Tightest Qualifiers")); } } } private void analyzeMethod(SootMethod sm){ CallGraph cg = Scene.v().getCallGraph(); //Iterator eIt = Scene.v().getEntryPoints().iterator(); //while (eIt.hasNext()){ // System.out.println(eIt.next()); //} if( methodToContexts == null ) { methodToContexts = new MethodToContexts( Scene.v().getReachableMethods().listener() ); } for( Iterator momcIt = methodToContexts.get(sm).iterator(); momcIt.hasNext(); ) { final MethodOrMethodContext momc = (MethodOrMethodContext) momcIt.next(); Iterator callerEdges = cg.edgesInto(momc); while (callerEdges.hasNext()){ Edge callEdge = (Edge)callerEdges.next(); if (!callEdge.isExplicit()) continue; SootMethod methodCaller = callEdge.src(); //System.out.println("Caller edge type: "+Edge.kindToString(callEdge.kind())); SootClass callingClass = methodCaller.getDeclaringClass(); // public methods if (Modifier.isPublic(sm.getModifiers())){ analyzePublicMethod(sm, callingClass); } // protected methods else if (Modifier.isProtected(sm.getModifiers())){ analyzeProtectedMethod(sm, callingClass); } // private methods - do nothing else if (Modifier.isPrivate(sm.getModifiers())){ } // package level methods else { analyzePackageMethod(sm, callingClass); } } } } private boolean analyzeProtectedMethod(SootMethod sm, SootClass callingClass){ SootClass methodClass = sm.getDeclaringClass(); //System.out.println("protected method: "+sm.getName()+" in class: "+methodClass.getName()+" calling class: "+callingClass.getName()); boolean insidePackageAccess = isCallSamePackage(callingClass, methodClass); boolean subClassAccess = isCallClassSubClass(callingClass, methodClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, methodClass); if (!insidePackageAccess && subClassAccess) { methodResultsMap.put(sm, new Integer(RESULT_PROTECTED)); return true; } else if (insidePackageAccess && !sameClassAccess) { updateToPackage(sm); return false; } else { updateToPrivate(sm); return false; } } private boolean analyzePackageMethod(SootMethod sm, SootClass callingClass){ SootClass methodClass = sm.getDeclaringClass(); //System.out.println("package method: "+sm.getName()+" in class: "+methodClass.getName()+" calling class: "+callingClass.getName()); boolean insidePackageAccess = isCallSamePackage(callingClass, methodClass); boolean subClassAccess = isCallClassSubClass(callingClass, methodClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, methodClass); if (insidePackageAccess && !sameClassAccess) { updateToPackage(sm); return true; } else { updateToPrivate(sm); return false; } } private boolean analyzePublicMethod(SootMethod sm, SootClass callingClass){ SootClass methodClass = sm.getDeclaringClass(); //System.out.println("public method: "+sm.getName()+" in class: "+methodClass.getName()+" calling class: "+callingClass.getName()); boolean insidePackageAccess = isCallSamePackage(callingClass, methodClass); boolean subClassAccess = isCallClassSubClass(callingClass, methodClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, methodClass); if (!insidePackageAccess && !subClassAccess){ methodResultsMap.put(sm, new Integer(RESULT_PUBLIC)); return true; } else if (!insidePackageAccess && subClassAccess) { updateToProtected(sm); return false; } else if (insidePackageAccess && !sameClassAccess) { updateToPackage(sm); return false; } else { updateToPrivate(sm); return false; } } private void updateToProtected(SootMethod sm){ if (!methodResultsMap.containsKey(sm)){ methodResultsMap.put(sm, new Integer(RESULT_PROTECTED)); } else { if (methodResultsMap.get(sm).intValue() != RESULT_PUBLIC){ methodResultsMap.put(sm, new Integer(RESULT_PROTECTED)); } } } private void updateToPackage(SootMethod sm){ if (!methodResultsMap.containsKey(sm)){ methodResultsMap.put(sm, new Integer(RESULT_PACKAGE)); } else { if (methodResultsMap.get(sm).intValue() == RESULT_PRIVATE){ methodResultsMap.put(sm, new Integer(RESULT_PACKAGE)); } } } private void updateToPrivate(SootMethod sm){ if (!methodResultsMap.containsKey(sm)) { methodResultsMap.put(sm, new Integer(RESULT_PRIVATE)); } } private boolean isCallClassMethodClass(SootClass call, SootClass check){ if (call.equals(check)) return true; return false; } private boolean isCallClassSubClass(SootClass call, SootClass check){ if (!call.hasSuperclass()) return false; if (call.getSuperclass().equals(check)) return true; return false; } private boolean isCallSamePackage(SootClass call, SootClass check){ if (call.getPackageName().equals(check.getPackageName())) return true; return false; } private void handleFields(){ Iterator classesIt = Scene.v().getApplicationClasses().iterator(); while (classesIt.hasNext()){ SootClass appClass = (SootClass)classesIt.next(); Iterator fieldsIt = appClass.getFields().iterator(); while (fieldsIt.hasNext()){ SootField sf = (SootField)fieldsIt.next(); analyzeField(sf); } } Iterator<SootField> fieldStatIt = fieldResultsMap.keySet().iterator(); while (fieldStatIt.hasNext()) { SootField f = fieldStatIt.next(); int result = fieldResultsMap.get(f).intValue(); String sRes = "Public"; if (result == RESULT_PUBLIC){ sRes = "Public"; } else if (result == RESULT_PROTECTED){ sRes = "Protected"; } else if (result == RESULT_PACKAGE){ sRes = "Package"; } else if (result == RESULT_PRIVATE){ sRes = "Private"; } String actual = null; if (Modifier.isPublic(f.getModifiers())){ //System.out.println("Field: "+f.getName()+" is public"); actual = "Public"; } else if (Modifier.isProtected(f.getModifiers())){ actual = "Protected"; } else if (Modifier.isPrivate(f.getModifiers())){ actual = "Private"; } else { actual = "Package"; } //System.out.println("Field: "+f.getName()+" has "+actual+" level access, can have: "+sRes+" level access."); if (!sRes.equals(actual)){ f.addTag(new StringTag("Field: "+f.getName()+" has "+actual+" level access, can have: "+sRes+" level access.", "Tightest Qualifiers")); f.addTag(new ColorTag(255, 10, 0, true, "Tightest Qualifiers")); } } } private void analyzeField(SootField sf){ // from all bodies get all use boxes and eliminate used fields Iterator classesIt = Scene.v().getApplicationClasses().iterator(); while (classesIt.hasNext()) { SootClass appClass = (SootClass)classesIt.next(); Iterator mIt = appClass.getMethods().iterator(); while (mIt.hasNext()) { SootMethod sm = (SootMethod)mIt.next(); if (!sm.hasActiveBody()) continue; if (!Scene.v().getReachableMethods().contains(sm)) continue; Body b = sm.getActiveBody(); Iterator usesIt = b.getUseBoxes().iterator(); while (usesIt.hasNext()) { ValueBox vBox = (ValueBox)usesIt.next(); Value v = vBox.getValue(); if (v instanceof FieldRef) { FieldRef fieldRef = (FieldRef)v; SootField f = fieldRef.getField(); if (f.equals(sf)) { if (Modifier.isPublic(sf.getModifiers())) { if (analyzePublicField(sf, appClass)) return; } else if (Modifier.isProtected(sf.getModifiers())) { analyzeProtectedField(sf, appClass); } else if(Modifier.isPrivate(sf.getModifiers())) { } else { analyzePackageField(sf, appClass); } } } } } } } private boolean analyzePublicField(SootField sf, SootClass callingClass){ SootClass fieldClass = sf.getDeclaringClass(); boolean insidePackageAccess = isCallSamePackage(callingClass, fieldClass); boolean subClassAccess = isCallClassSubClass(callingClass, fieldClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, fieldClass); if (!insidePackageAccess && !subClassAccess){ fieldResultsMap.put(sf, new Integer(RESULT_PUBLIC)); return true; } else if (!insidePackageAccess && subClassAccess) { updateToProtected(sf); return false; } else if (insidePackageAccess && !sameClassAccess) { updateToPackage(sf); return false; } else { updateToPrivate(sf); return false; } } private boolean analyzeProtectedField(SootField sf, SootClass callingClass){ SootClass fieldClass = sf.getDeclaringClass(); boolean insidePackageAccess = isCallSamePackage(callingClass, fieldClass); boolean subClassAccess = isCallClassSubClass(callingClass, fieldClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, fieldClass); if (!insidePackageAccess && subClassAccess) { fieldResultsMap.put(sf, new Integer(RESULT_PROTECTED)); return true; } else if (insidePackageAccess && !sameClassAccess) { updateToPackage(sf); return false; } else { updateToPrivate(sf); return false; } } private boolean analyzePackageField(SootField sf, SootClass callingClass){ SootClass fieldClass = sf.getDeclaringClass(); boolean insidePackageAccess = isCallSamePackage(callingClass, fieldClass); boolean subClassAccess = isCallClassSubClass(callingClass, fieldClass); boolean sameClassAccess = isCallClassMethodClass(callingClass, fieldClass); if (insidePackageAccess && !sameClassAccess) { updateToPackage(sf); return true; } else { updateToPrivate(sf); return false; } } private void updateToProtected(SootField sf){ if (!fieldResultsMap.containsKey(sf)){ fieldResultsMap.put(sf, new Integer(RESULT_PROTECTED)); } else { if (fieldResultsMap.get(sf).intValue() != RESULT_PUBLIC){ fieldResultsMap.put(sf, new Integer(RESULT_PROTECTED)); } } } private void updateToPackage(SootField sf){ if (!fieldResultsMap.containsKey(sf)){ fieldResultsMap.put(sf, new Integer(RESULT_PACKAGE)); } else { if (fieldResultsMap.get(sf).intValue() == RESULT_PRIVATE){ fieldResultsMap.put(sf, new Integer(RESULT_PACKAGE)); } } } private void updateToPrivate(SootField sf){ if (!fieldResultsMap.containsKey(sf)) { fieldResultsMap.put(sf, new Integer(RESULT_PRIVATE)); } } }