/* 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));
}
}
}