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