Package dk.brics.string.annotation

Source Code of dk.brics.string.annotation.AnnotationAnalyzer

package dk.brics.string.annotation;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import soot.Body;
import soot.Hierarchy;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.AbstractStmtSwitch;
import soot.jimple.AssignStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.InvokeExpr;
import soot.jimple.ParameterRef;
import soot.jimple.ReturnStmt;
import soot.jimple.Stmt;
import soot.jimple.internal.JInstanceFieldRef;
import soot.tagkit.AnnotationStringElem;
import soot.tagkit.AnnotationTag;
import soot.tagkit.VisibilityAnnotationTag;
import soot.tagkit.VisibilityParameterAnnotationTag;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import dk.brics.string.BindingAutomatonProvider;
import dk.brics.string.java.StaticStringTypes;

public class AnnotationAnalyzer extends AbstractStmtSwitch implements StaticStringTypes {
 
  //Maps an artifact like field, return or parameter to a declared @Type annotation
  private Map<SootField,Automaton> fieldAutomatonMap = new HashMap<SootField,Automaton>();
 
  /** The annotations placed directly at the return type */
  private Map<SootMethod,Automaton> returnAutomatonMap = new HashMap<SootMethod,Automaton>();
 
  /** The annotations placed or inherited at the return type */
    private Map<SootMethod,Automaton> returnAutomatonMapFull = new HashMap<SootMethod,Automaton>();
   
    /** The annotations placed directly on each parameter */
  private Map<SootMethod,Map<Integer,Automaton>> parameterAutomatonMap = new HashMap<SootMethod,Map<Integer,Automaton>>();
 
  /** The annotations placed or inherited at each parameter */
    private Map<SootMethod,Map<Integer,Automaton>> parameterAutomatonMapFull = new HashMap<SootMethod,Map<Integer,Automaton>>();
 
  private Set<ValueBox> hotspots = new HashSet<ValueBox>();
  private Map<ValueBox,Automaton> boxAutomatonMap = new HashMap<ValueBox,Automaton>();
  private Map<ValueBox,Stmt> boxStmtMap = new HashMap<ValueBox,Stmt>();
 
  private Map<Automaton,String> automatonDescriptionMap = new HashMap<Automaton,String>();
 
  private BindingAutomatonProvider bindings;
 
  private SootMethod currentMethod;
 
  private List<StaticTypeError> errors = new ArrayList<StaticTypeError>();

  public AnnotationAnalyzer() {
      this(new BindingAutomatonProvider());
  }
 
    @SuppressWarnings("unchecked")
  public AnnotationAnalyzer(BindingAutomatonProvider bindings) {
      this.bindings = bindings;
     
    //Find @Type and @LoadType annotations and add to a map
    Collection<SootClass> classes = Scene.v().getApplicationClasses();
    Hierarchy h = Scene.v().getActiveHierarchy();
    if (h == null)
        h = new Hierarchy();
    for (SootClass sootClass : classes) {
      //Find fields containing the @Type annotation and add to map
      Collection<SootField> fields = sootClass.getFields();
      for (SootField field : fields) {
        if (field.hasTag("VisibilityAnnotationTag")) {
          VisibilityAnnotationTag vat = (VisibilityAnnotationTag)field.getTag("VisibilityAnnotationTag");
          Automaton automaton = getAutomatonFromTag(vat);
          if (automaton != null) {
            fieldAutomatonMap.put(field, automaton);
          }
        }
        //Remove all tags from field. JSA tags will be added later
        field.removeAllTags();
      }
     
      //Find methods containing @Type or @LoadType annotation and add to map
      Collection<SootMethod> methods = sootClass.getMethods();
      for (SootMethod m : methods) {
        //Find return annotation
        if (isStringType(m.getReturnType())) {
          VisibilityAnnotationTag vat = (VisibilityAnnotationTag)m.getTag("VisibilityAnnotationTag");
          if (vat != null) {
            Automaton automaton = getAutomatonFromTag(vat);
            if (automaton != null) {
                returnAutomatonMap.put(m, automaton);
            }
          }
        }
       
        //Find parameter annotations
        int count = m.getParameterCount();
        for (int i = 0; i<count; i++) {
          if (isStringType(m.getParameterType(i))) {
            VisibilityParameterAnnotationTag vpat = (VisibilityParameterAnnotationTag)m.getTag("VisibilityParameterAnnotationTag");
            if (vpat != null) {
              VisibilityAnnotationTag vat = vpat.getVisibilityAnnotations().get(i);
              Automaton automaton = getAutomatonFromTag(vat);
              if (automaton != null) {
                Map<Integer,Automaton> pMap;
                if (parameterAutomatonMap.containsKey(m)) {
                  pMap = parameterAutomatonMap.get(m);
                }
                else {
                  pMap = new HashMap<Integer,Automaton>();
                  parameterAutomatonMap.put(m, pMap);
                }
                pMap.put(i, automaton);
              }
            }
          }
        }
        //Remove all tags from method. JSA tags will be added later
        m.removeAllTags();
      }
    }
   
    // apply inheritance relations to construct the 'full' maps
    for (SootClass sootClass : classes) {
        for (SootMethod m1 : sootClass.getMethods()) {
            Automaton m1Returns = returnAutomatonMap.get(m1);
            Map<Integer, Automaton> m1Parameters = parameterAutomatonMap.get(m1);
            //if (m1Returns == null && m1Parameters == null)
            //    continue;
            if (m1Parameters == null)
              m1Parameters = Collections.emptyMap();
            for (SootMethod m2 : (List<SootMethod>)h.resolveAbstractDispatch(sootClass, m1)) {
                // HANDLE RETURN TYPES
                if (m1Returns != null) {
                    inheritReturnType(m1, m1Returns, m2);
                }
               
                // HANDLE PARAMETER TYPES
                //if (m1Parameters != null) {
                      if (!parameterAutomatonMapFull.containsKey(m2)) {
                          parameterAutomatonMapFull.put(m2, new HashMap<Integer,Automaton>());
                      }
                    for (int i=0; i<m1.getParameterCount(); i++) {
                        Automaton m1Parameter = m1Parameters.get(i);
                        if (m1Parameter == null) {
                          // ensure that the child's parameter has no annotation
                          Map<Integer,Automaton> m2Parameters = parameterAutomatonMap.get(m2);
                          if (m2Parameters != null && m2Parameters.containsKey(i)) {
                            errorSuperParameterNotAnnotated(m2, m1, i);
                          }
                        } else {
                          inheritParameterType(m1, m2, i, m1Parameter);
                        }
                    }
                //}
            }
        }
    }
   
    //Visit every statement to check if it is a hotspot
    for (SootClass sootClass : classes) {
      Collection<SootMethod> methods = sootClass.getMethods();
      for (SootMethod method : methods) {
        currentMethod = method;
        if (method.hasActiveBody()) {
          Body body = method.getActiveBody();
          Collection<Unit> uc = body.getUnits();
          for (Unit u : uc) {
            if (u instanceof Stmt) {
              Stmt stmt = (Stmt)u;
              stmt.apply(this);
              if (stmt.containsInvokeExpr()) {
                createHotspotsFromInvokeExpr(stmt);
              }
            }
          }
        }
      }
    }
  }

    private void inheritParameterType(SootMethod m1, SootMethod m2, int paramIndex, Automaton m1Parameter) {
        Map<Integer,Automaton> m2Parameters = parameterAutomatonMap.get(m2);
        Automaton m2Parameter = (m2Parameters == null) ? null : m2Parameters.get(paramIndex);
       
        if (m2Parameter == null) {
            Automaton m2ParameterFull = parameterAutomatonMapFull.get(m2).get(paramIndex);
            if (m2ParameterFull == null) {
                // if super method's parameter is not annotated, then any string must be accepted
                // by the child method
                parameterAutomatonMapFull.get(m2).put(paramIndex, Automaton.makeAnyString());
            } else {
                parameterAutomatonMapFull.get(m2).put(paramIndex, m1Parameter.union(m2ParameterFull));
            }
        } else {
            if (!m1Parameter.subsetOf(m2Parameter)) {
                errorParameterMoreRestrictive(m2, m1, paramIndex);
            }
            parameterAutomatonMapFull.get(m2).put(paramIndex, m2Parameter);
        }
    }
   
    private void inheritReturnType(SootMethod m1, Automaton m1Returns, SootMethod m2) {
        Automaton m2Returns = returnAutomatonMap.get(m2);
       
        // if subtype has no annotation, inherit this one
        if (m2Returns == null) {
            Automaton m2full = returnAutomatonMapFull.get(m2);
            if (m2full == null) {
                returnAutomatonMapFull.put(m2, m1Returns);
            } else {
                // if inheriting for more than one place, intersect the languages
                returnAutomatonMapFull.put(m2, m1Returns.intersection(m2full));
            }
        } else {
            if (!m2Returns.subsetOf(m1Returns)) {
                errorReturnTypeLessRestrictive(m2, m1);
            }
           
            returnAutomatonMapFull.put(m2, m2Returns);
        }
    }
   
  private void errorReturnTypeLessRestrictive(SootMethod childMethod, SootMethod superMethod) {
        errors.add(new StaticTypeError(
                childMethod.getName()
                + " has a less restrictive return type in "
                + childMethod.getDeclaringClass().getName()
                + " than in the super class "
                + superMethod.getDeclaringClass().getName()));
  }
    private void errorParameterMoreRestrictive(SootMethod childMethod, SootMethod superMethod, int paramIndex) {
        errors.add(new StaticTypeError(
                childMethod.getName()
                + " has a more restrictive parameter type for parameter #" + (paramIndex+1) + " in " // 1-based index in readable messages
                + childMethod.getDeclaringClass().getName()
                + " than in the super class "
                + superMethod.getDeclaringClass().getName()));
    }
    private void errorSuperParameterNotAnnotated(SootMethod childMethod, SootMethod superMethod, int paramIndex) {
      errors.add(new StaticTypeError(
          childMethod.getName()
          + " may not annotate parameter #" + (paramIndex+1) + " in "
          + childMethod.getDeclaringClass().getName()
          + " because it is not annotated in super class "
          + superMethod.getDeclaringClass().getName()
          ));
    }
 
  public Set<ValueBox> getHotspots() {
    return hotspots;
  }
 
  public Automaton getAutomaton(ValueBox box) {
    return boxAutomatonMap.get(box);
  }
 
  public String getDescription(Automaton a) {
    return automatonDescriptionMap.get(a);
  }
 
  public Stmt getStmt(ValueBox box) {
    return boxStmtMap.get(box);
  }
 
  private boolean isStringType(soot.Type t) {
    if (t instanceof RefType)
      if (((RefType)t).getSootClass().getName().equals("java.lang.String"))
        return true;
    return false;
  }
 
  private Automaton getAutomatonFromTag(VisibilityAnnotationTag vat) {
    Collection<AnnotationTag> annotations = vat.getAnnotations();
    if (annotations != null) {
      for (AnnotationTag at: annotations) {
        String type = at.getType().trim();
        if (type.equals("Ldk/brics/string/annotation/Type;") && at.getNumElems() == 1) {
            // XXX why are we trimming the regexp here?? Although rare, it is perfectly sane for a string-type
            // to end with blanks. E.g @Type("Hello ") would become @Type("Hello").
          String pattern = ((AnnotationStringElem)at.getElemAt(0)).getValue().trim();
          Automaton a = (new RegExp(pattern)).toAutomaton(bindings);
          automatonDescriptionMap.put(a, pattern);
          return a;
        }
        if (type.equals("Ldk/brics/string/annotation/LoadType;") && at.getNumElems() == 1) {
          String path = ((AnnotationStringElem)at.getElemAt(0)).getValue().trim();
          Automaton a = loadAutomaton(path);
          if (a != null) {
            automatonDescriptionMap.put(a, "automaton '" + path + "'");
            return a;
          }
          else {
            return null;
          }
        }
      }
    }
    return null;
  }
 
  private Automaton loadAutomaton(String path) {
    try {
      //Try to load automaton
      ObjectInputStream in = new ObjectInputStream(new FileInputStream(path));
      Automaton a = (Automaton) in.readObject();
      return a;
    }
    catch (Exception e) {
      //Load failed.
      String error = "Loading automaton '" + path + "' failed!";
      System.err.println(error);
      return null;
    }
  }
 
  private void createHotspot(Stmt stmt, ValueBox box, Automaton automaton) {
    hotspots.add(box);
    boxStmtMap.put(box,stmt);
    boxAutomatonMap.put(box,automaton);
  }
 
  public void createHotspotsFromInvokeExpr(Stmt stmt) {
    InvokeExpr expr = stmt.getInvokeExpr();
    SootMethod target = expr.getMethod();
    for (int i=0; i<expr.getArgCount(); i++) {
      Value value = expr.getArg(i);
      ValueBox box = expr.getArgBox(i);
      if (isStringType(value.getType())) {
        Map<Integer,Automaton> pMap = parameterAutomatonMap.get(target);
        if (pMap != null && pMap.containsKey(i)) {
          Automaton automaton = pMap.get(i);
          createHotspot(stmt, box, automaton);
        }
      }
    }
  }
 
  @Override
  public void caseAssignStmt(AssignStmt stmt) {
    Value lvalue = stmt.getLeftOp();
    ValueBox rbox = stmt.getRightOpBox();
    if (lvalue instanceof JInstanceFieldRef) {
      JInstanceFieldRef ref = (JInstanceFieldRef)lvalue;
      SootField field = ref.getField();
      if (fieldAutomatonMap.containsKey(field)) {
        Automaton automaton = fieldAutomatonMap.get(field);
        createHotspot(stmt, rbox, automaton);
      }
    }
  }
 
  @Override
  public void caseIdentityStmt(IdentityStmt stmt) {
    Value rvalue = stmt.getRightOp();
    ValueBox rbox = stmt.getRightOpBox();
    if (rvalue instanceof ParameterRef) {
      ParameterRef parameter = (ParameterRef)rvalue;
      int index = parameter.getIndex();
      Map<Integer,Automaton> pMap = parameterAutomatonMap.get(currentMethod);
      if (pMap != null && pMap.containsKey(index)) {
        Automaton automaton = pMap.get(index);
        createHotspot(stmt, rbox, automaton);
      }
    }
  }
 
  @Override
  public void caseReturnStmt(ReturnStmt stmt) {
    if (returnAutomatonMap.containsKey(currentMethod)) {
      ValueBox box = stmt.getOpBox();
      Automaton automaton = returnAutomatonMap.get(currentMethod);
      createHotspot(stmt, box, automaton);
    }
  }

    public Automaton getFieldType(SootField field) {
        return fieldAutomatonMap.get(field);
    }

    public Automaton getMethodReturnType(SootMethod method) {
        return returnAutomatonMapFull.get(method);
    }

    public Automaton getParameterType(SootMethod method, int paramIndex) {
        Map<Integer, Automaton> map = parameterAutomatonMapFull.get(method);
        if (map != null) {
            return map.get(paramIndex);
        } else {
            return null;
        }
    }

    public List<StaticTypeError> getErrors() {
        return Collections.unmodifiableList(errors);
    }
   
   
 
 
}
TOP

Related Classes of dk.brics.string.annotation.AnnotationAnalyzer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.