Home | History | Annotate | Download | only in scanner
      1 package annotator.scanner;
      2 
      3 import java.util.ArrayList;
      4 import java.util.HashMap;
      5 import java.util.List;
      6 import java.util.Map;
      7 
      8 import com.sun.source.tree.Tree;
      9 import com.sun.source.tree.VariableTree;
     10 import com.sun.source.util.TreePath;
     11 import com.sun.tools.javac.util.Pair;
     12 
     13 /** LocalVariableScanner stores information about the names and offsets of
     14  * local variables inside a method, and can also be used to scan the source
     15  * tree and determine the index of a local variable with a given name, so that
     16  * the i^th index corresponds to the i^th declaration of a local variable with
     17  * that name, using 0-based indexing.
     18  */
     19 public class LocalVariableScanner extends CommonScanner {
     20   /**
     21    * Computes the index i of the given tree along the given tree path
     22    * such that it is the i^th declaration of the local variable with the given
     23    * var name, using 0-based indexing.
     24    *
     25    * @param origpath the source path that ends in varTree
     26    * @param varTree the variable tree that declares the local variable
     27    * @param varName the name of the local variable
     28    * @return the index of the variable tree with respect to the given
     29    *  local variable name
     30    */
     31   public static int indexOfVarTree(TreePath origpath, Tree varTree, String varName) {
     32     TreePath path = findCountingContext(origpath);
     33     if (path == null) {
     34       return -1;
     35     }
     36 
     37     LocalVariableScanner lvts = new LocalVariableScanner(varTree, varName);
     38 
     39     try {
     40       lvts.scan(path, null);
     41     } catch (Throwable e) {
     42       System.out.println("LocalVariableScanner: can't locate: " + varTree);
     43       return -2; // Don't return -1, which is above return code
     44     }
     45     return lvts.index;
     46   }
     47 
     48   /*
     49    * For efficiency, we might want to have methods like the following, specialized for the
     50    * three different cases.
     51    */
     52   /*
     53   public static int indexOfVarTreeInStaticInit(TreePath path, Tree tree, String varName) {
     54     // only start searching from within this method
     55       path = findEnclosingStaticInit(path);
     56       if (path == null) {
     57         return -1;
     58       }
     59 
     60       LocalVariableScanner lvts = new LocalVariableScanner(tree, varName);
     61       lvts.scan(path, null);
     62       return lvts.index;
     63   }
     64   */
     65 
     66   private int index = -1;
     67   private boolean done = false;
     68   private final Tree varTree;
     69   private final String varName;
     70 
     71   private LocalVariableScanner(Tree varTree, String varName) {
     72     this.index = -1;
     73     this.done = false;
     74     this.varTree = varTree;
     75     this.varName = varName;
     76   }
     77 
     78   @Override
     79   public Void visitVariable(VariableTree node, Void p) {
     80     // increment index only if you have not already reached the right node, and
     81     // if this node declares the same local variable you are searching for
     82     if (varName.equals(node.getName().toString())) {
     83       if (!done) {
     84         index++;
     85       }
     86       if (varTree == node) {
     87         done = true;
     88       }
     89     }
     90     return p;
     91   }
     92 
     93   // TODO: refactor class keys to avoid so many uses of generics
     94 
     95   // mapping from (method-name, variable-index, start-offset)
     96   // to variable name
     97   private static Map<Pair<String, Pair<Integer,Integer>>, String>
     98     methodNameIndexMap = new HashMap<Pair<String, Pair<Integer,Integer>>, String>();
     99 
    100   // map from method to map from variable name to
    101   // a list of start offsets
    102   private static Map<String, Map<String, List<Integer>>>
    103     methodNameCounter = new HashMap<String, Map<String,List<Integer>>>();
    104 
    105   /**
    106    * Adds the given variable specified as a pair of method name and
    107    *  (index, start-offset) under the given name to the list of all local
    108    *  variables.
    109    *
    110    * @param varInfo a pair of the method and a pair describing the local
    111    *  variable index and start offset of the local variable
    112    * @param name the name of the local variable
    113    */
    114   public static void addToMethodNameIndexMap(Pair<String, Pair<Integer,Integer>> varInfo, String name) {
    115     methodNameIndexMap.put(varInfo, name);
    116   }
    117 
    118   /**
    119    * Gets the name of the local variable in the given method, and at the
    120    *  the given index and offset.
    121    *
    122    * @param varInfo a pair of the method name and a pair of the local variable's
    123    *  index and start offset
    124    * @return the name of the local variable at the specified location
    125    */
    126   public static String getFromMethodNameIndexMap(Pair<String, Pair<Integer, Integer>> varInfo) {
    127     return methodNameIndexMap.get(varInfo);
    128   }
    129 
    130   /**
    131    * Adds to the given method the fact that the local variable with the given
    132    *  name is declared at the given start offset.
    133    *
    134    * @param methodName the method containing the local variable
    135    * @param varName the name of the local variable
    136    * @param offset the start offset of the local variable
    137    */
    138   public static void addToMethodNameCounter(String methodName, String varName,
    139         Integer offset) {
    140     Map<String, List<Integer>> nameOffsetCounter = methodNameCounter.get(methodName);
    141     if (nameOffsetCounter == null) {
    142       nameOffsetCounter = new HashMap<String, List<Integer>>();
    143       methodNameCounter.put(methodName, nameOffsetCounter);
    144     }
    145 
    146     List<  Integer> listOfOffsets = nameOffsetCounter.get(varName);
    147     if (listOfOffsets == null) {
    148       listOfOffsets = new ArrayList<  Integer>();
    149       nameOffsetCounter.put(varName, listOfOffsets);
    150     }
    151 
    152     listOfOffsets.add(offset);
    153   }
    154 
    155   /**
    156    * Returns a list of all start bytecode offsets of variable declarations with
    157    * the given variable name in the given method.
    158    *
    159    * @param methodName the name of the method
    160    * @param varName the name of the local variable
    161    * @return a list of start offsets for live ranges of all local variables
    162    * with the given name in the given method
    163    */
    164   public static List<Integer> getFromMethodNameCounter(String methodName, String varName) {
    165     return methodNameCounter.get(methodName).get(varName);
    166   }
    167 }
    168