1 cananian 1.1.2.1 // MayReadOracle.java, created Wed Nov 7 10:38:43 2001 by cananian 2 cananian 1.1.2.1 // Copyright (C) 2000 C. Scott Ananian <cananian@alumni.princeton.edu> 3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details. 4 cananian 1.1.2.1 package harpoon.Analysis.Quads; 5 cananian 1.1.2.1 6 cananian 1.1.2.1 import harpoon.ClassFile.HClass; 7 cananian 1.1.2.1 import harpoon.ClassFile.HCode; 8 cananian 1.1.2.3 import harpoon.ClassFile.HConstructor; 9 cananian 1.1.2.1 import harpoon.ClassFile.HField; 10 cananian 1.1.2.1 import harpoon.ClassFile.HMethod; 11 cananian 1.1.2.1 import harpoon.IR.Quads.CALL; 12 cananian 1.1.2.1 import harpoon.IR.Quads.Edge; 13 cananian 1.1.2.1 import harpoon.IR.Quads.GET; 14 cananian 1.1.2.1 import harpoon.IR.Quads.HEADER; 15 cananian 1.1.2.1 import harpoon.IR.Quads.PHI; 16 cananian 1.1.2.1 import harpoon.IR.Quads.Quad; 17 cananian 1.1.2.1 import harpoon.IR.Quads.QuadVisitor; 18 cananian 1.1.2.1 import harpoon.IR.Quads.RETURN; 19 cananian 1.1.2.1 import harpoon.IR.Quads.SIGMA; 20 cananian 1.1.2.1 import harpoon.IR.Quads.THROW; 21 cananian 1.5 import net.cscott.jutil.DisjointSet; 22 cananian 1.1.2.1 import harpoon.Util.Util; 23 cananian 1.5 import net.cscott.jutil.WorkSet; 24 cananian 1.5 import net.cscott.jutil.AggregateSetFactory; 25 cananian 1.5 import net.cscott.jutil.SetFactory; 26 cananian 1.1.2.1 27 cananian 1.1.2.1 import java.util.Collections; 28 cananian 1.1.2.1 import java.util.HashMap; 29 cananian 1.1.2.1 import java.util.HashSet; 30 cananian 1.1.2.1 import java.util.Iterator; 31 cananian 1.1.2.1 import java.util.Map; 32 cananian 1.1.2.1 import java.util.Set; 33 cananian 1.1.2.1 34 cananian 1.1.2.1 /** 35 cananian 1.1.2.1 * A <code>MayReadOracle</code> tells you which fields of a given 36 cananian 1.1.2.1 * class 'may' be read on the given edge. 37 cananian 1.1.2.1 * 38 cananian 1.1.2.1 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 39 cananian 1.5 * @version $Id: MayReadOracle.java,v 1.5 2004/02/08 01:53:14 cananian Exp $ 40 cananian 1.1.2.1 */ 41 cananian 1.1.2.1 public class MayReadOracle { 42 cananian 1.1.2.1 final Map results = new HashMap(); 43 cananian 1.1.2.1 final DisjointSet aliasMap = new DisjointSet(); 44 cananian 1.1.2.1 45 cananian 1.1.2.1 /** Returns the set of fields possibly-read before edge <code>e</code> 46 cananian 1.1.2.1 * is executed. The edge must belong to the <code>HCode</code> 47 cananian 1.1.2.1 * given to this class' constructor. */ 48 cananian 1.1.2.1 public Set mayReadAt(Edge e) { 49 cananian 1.1.2.1 EdgeProp ep = (EdgeProp) results.get(aliasMap.find(e)); 50 cananian 1.3.2.1 assert ep!=null : e; 51 cananian 1.1.2.1 return Collections.unmodifiableSet(ep.reads); 52 cananian 1.1.2.1 } 53 cananian 1.1.2.1 54 cananian 1.1.2.1 /** Creates a <code>MayReadOracle</code> using the quad-ssi representation 55 cananian 1.1.2.1 * <code>hc</code> of some method and global information about read fields 56 cananian 1.1.2.1 * provided by <code>fso</code>. We will return results for only 57 cananian 1.1.2.1 * fields declared by class <code>which</code>, unless <code>which</code> 58 cananian 1.1.2.3 * is null, in which case we'll return results for all fields. 59 cananian 1.1.2.3 * If <code>specialHandlingForConstructors</code> is <code>true</code>, 60 cananian 1.1.2.3 * then when analyzing a constructor we will only consider fields of 61 cananian 1.1.2.3 * objects which *may* be <code>this</code> (parameter 0 of the 62 cananian 1.1.2.3 * constructor) as reads. This is useful if we are trying to determine 63 cananian 1.1.2.3 * whether a field is read before it is defined in a constructor, 64 cananian 1.1.2.3 * for example. With <code>specialHandlingForConstructors</code> equal 65 cananian 1.1.2.3 * to <code>false</code>, no reads are ignored. 66 cananian 1.1.2.3 */ 67 cananian 1.1.2.1 public MayReadOracle(HCode hc, FieldSyncOracle fso, 68 cananian 1.1.2.3 CallGraph cg, HClass which, 69 cananian 1.1.2.3 boolean specialHandlingForConstructors) { 70 cananian 1.1.2.3 // we only need a mustparamoracle if special handling and hc 71 cananian 1.1.2.3 // corresponds to a constructor. 72 cananian 1.1.2.3 MustParamOracle mpo = null; 73 cananian 1.1.2.3 if (specialHandlingForConstructors && isConstructor(hc.getMethod())) 74 cananian 1.1.2.3 mpo = new MustParamOracle(hc); 75 cananian 1.1.2.1 // create new visitor 76 cananian 1.1.2.3 ReadVisitor rv = new ReadVisitor(which, hc.getMethod(), fso, cg, mpo); 77 cananian 1.1.2.1 // add HEADER quad to work list. 78 cananian 1.1.2.1 rv.W.add(hc.getRootElement()); 79 cananian 1.1.2.1 // iterate until worklist is empty. 80 cananian 1.1.2.1 while (!rv.W.isEmpty()) 81 cananian 1.1.2.1 ((Quad)rv.W.pop()).accept(rv); 82 cananian 1.1.2.1 // done! all results are in rv.results map (w/ 'aliasMap') 83 cananian 1.1.2.1 } 84 cananian 1.1.2.1 /** 'reads' set of a edge, along with which 'next' quad to 85 cananian 1.1.2.1 * evaluate if/when the reads-set changes. */ 86 cananian 1.1.2.1 static class EdgeProp { 87 cananian 1.1.2.1 final Set reads; 88 cananian 1.1.2.1 Quad next; // updatable. 89 cananian 1.1.2.1 EdgeProp(Set reads, Quad next) { 90 cananian 1.1.2.1 this.reads=reads; this.next=next; 91 cananian 1.1.2.1 } 92 cananian 1.1.2.1 } 93 cananian 1.1.2.1 /** this is the visitor class which does the real work. 94 cananian 1.1.2.1 * Theory of operation: straight data-flow on edges. If any 95 cananian 1.1.2.1 * quad modifies its outset, it adds the next quad to the worklist. 96 cananian 1.1.2.1 * We continue until worklist is empty. To speed iteration, any 97 cananian 1.1.2.1 * quad which does not change the outset (i.e. the transfer function 98 cananian 1.1.2.1 * between its in-set and out-set is the identity function) is 99 cananian 1.1.2.1 * skipped over in subsequent passes by aliasing its in-edge to its 100 cananian 1.1.2.1 * out-edge. */ 101 cananian 1.1.2.1 class ReadVisitor extends QuadVisitor { 102 cananian 1.1.2.1 final HClass declaringClass; 103 cananian 1.1.2.1 final HMethod thisMethod; 104 cananian 1.1.2.1 final WorkSet W = new WorkSet(); 105 cananian 1.1.2.1 final FieldSyncOracle fso; 106 cananian 1.1.2.3 final MustParamOracle mpo; // only non-null if special handling. 107 cananian 1.1.2.1 final CallGraph cg; 108 cananian 1.1.2.1 final SetFactory sf = new AggregateSetFactory(); 109 cananian 1.1.2.1 // our task: given an inset, create an outset. 110 cananian 1.1.2.1 // the inset corresponds to q.prev(x) (which is 'in') 111 cananian 1.1.2.1 // filtered through the aliasMap (which is 'real'). 112 cananian 1.1.2.1 ReadVisitor(HClass declaringClass, HMethod thisMethod, 113 cananian 1.1.2.3 FieldSyncOracle fso, CallGraph cg, MustParamOracle mpo) { 114 cananian 1.1.2.1 this.declaringClass = declaringClass; 115 cananian 1.1.2.1 this.thisMethod = thisMethod; 116 cananian 1.1.2.1 this.fso = fso; 117 cananian 1.1.2.3 this.mpo = mpo; 118 cananian 1.1.2.1 this.cg = cg; 119 cananian 1.1.2.1 } 120 cananian 1.1.2.1 /** initialize the analysis. HEADER generates our initial out-set. */ 121 cananian 1.1.2.1 public void visit(HEADER q) { 122 cananian 1.1.2.1 // no in-set. empty out-set. continue with METHOD (edge 1). 123 cananian 1.1.2.1 Edge out=q.nextEdge(1); 124 cananian 1.3.2.1 assert !results.containsKey(aliasMap.find(out)); 125 cananian 1.1.2.1 EdgeProp nep = new EdgeProp(Collections.EMPTY_SET, (Quad)out.to()); 126 cananian 1.1.2.1 results.put(aliasMap.find(out), nep); 127 cananian 1.1.2.1 W.add(nep.next); 128 cananian 1.1.2.1 } 129 cananian 1.1.2.1 /** this method handles all 'no-change' in-to-out transfer functions. 130 cananian 1.1.2.1 * we basically just map the in edge to the out edge, with some 131 cananian 1.1.2.1 * magic involved to get the results maps and such to work out 132 cananian 1.1.2.1 * right. */ 133 cananian 1.1.2.1 public void visit(Quad q) { 134 cananian 1.1.2.1 // one input, one output. add alias from in to out edge. 135 cananian 1.3.2.1 assert q.prevLength()==1 && q.nextLength()==1; 136 cananian 1.1.2.1 Edge in = q.prevEdge(0), out=q.nextEdge(0); 137 cananian 1.1.2.1 // fetch inset and next. 138 cananian 1.1.2.1 EdgeProp oep = (EdgeProp) results.remove(aliasMap.find(in)); 139 cananian 1.3.2.1 assert oep!=null; 140 cananian 1.1.2.1 // inset becomes outset (no change) but update 'next'. 141 cananian 1.1.2.1 EdgeProp nep = oep; 142 cananian 1.1.2.1 nep.next = q.next(0); 143 cananian 1.1.2.1 // merge 'out' with 'in'... 144 cananian 1.1.2.1 aliasMap.union(in, out); 145 cananian 1.1.2.1 // ...re-put outset. 146 cananian 1.1.2.1 results.put(aliasMap.find(out), nep); 147 cananian 1.1.2.1 // ta-da! 148 cananian 1.1.2.1 W.add(nep.next); 149 cananian 1.1.2.1 } 150 cananian 1.1.2.1 /** GET possibly adds a field to its out-set. */ 151 cananian 1.1.2.1 public void visit(GET q) { 152 cananian 1.1.2.1 // ignore if this get is not from the class we're interested in. 153 cananian 1.1.2.1 if (declaringClass != null && 154 cananian 1.1.2.1 !q.field().getDeclaringClass().equals(declaringClass)) { 155 cananian 1.1.2.1 visit((Quad)q); 156 cananian 1.1.2.1 return; 157 cananian 1.1.2.1 } 158 cananian 1.1.2.1 // outset = inset + this field. 159 cananian 1.1.2.1 boolean changed = false; 160 cananian 1.1.2.1 Edge in = q.prevEdge(0), out=q.nextEdge(0); 161 cananian 1.1.2.1 EdgeProp oep = (EdgeProp) results.get(aliasMap.find(in)); 162 cananian 1.3.2.1 assert oep!=null; 163 cananian 1.1.2.1 EdgeProp nep = (EdgeProp) results.get(aliasMap.find(out)); 164 cananian 1.1.2.1 if (nep==null) { 165 cananian 1.1.2.1 nep = new EdgeProp(sf.makeSet(oep.reads), q.next(0)); 166 cananian 1.1.2.1 results.put(aliasMap.find(out), nep); 167 cananian 1.1.2.1 changed = true; 168 cananian 1.1.2.1 } 169 cananian 1.1.2.3 if (isOkay(q) && nep.reads.add(q.field())) 170 cananian 1.1.2.1 changed = true; 171 cananian 1.1.2.1 // if something changed, add nep.next to work set. 172 cananian 1.1.2.1 if (changed) 173 cananian 1.1.2.1 W.add(nep.next); 174 cananian 1.1.2.1 } 175 cananian 1.1.2.3 /** this implements the 'special handling for constructors' */ 176 cananian 1.1.2.3 boolean isOkay(GET q) { 177 cananian 1.1.2.3 if (mpo==null) return true; // always okay if not special handling 178 cananian 1.1.2.3 return mpo.isMustParam(q.objectref()) && 179 cananian 1.1.2.3 mpo.whichMustParam(q.objectref())==0; 180 cananian 1.1.2.3 } 181 cananian 1.1.2.1 /** A CALL adds all fields possibly-read by the methods 182 cananian 1.1.2.1 * possibly-called by it to its out-set. */ 183 cananian 1.1.2.1 public void visit(CALL q) { 184 cananian 1.1.2.1 Edge in = q.prevEdge(0); 185 cananian 1.1.2.1 EdgeProp oep = (EdgeProp) results.get(aliasMap.find(in)); 186 cananian 1.3.2.1 assert oep!=null; 187 cananian 1.1.2.1 // find methods callable from q. 188 cananian 1.1.2.1 HMethod[] callable = cg.calls(thisMethod, q); 189 cananian 1.1.2.1 // any any fields which *may* be read by one of these methods. 190 cananian 1.1.2.1 Set mayRead = new HashSet(); 191 cananian 1.1.2.1 for (int i=0; i<callable.length; i++) 192 cananian 1.1.2.1 mayRead.addAll(fso.fieldsRead(callable[i])); 193 cananian 1.1.2.1 // filter out the fields we're not interested in. 194 cananian 1.1.2.1 if (declaringClass!=null) 195 cananian 1.1.2.1 for (Iterator it=mayRead.iterator(); it.hasNext(); ) 196 cananian 1.1.2.1 if (!((HField)it.next()).getDeclaringClass() 197 cananian 1.1.2.1 .equals(declaringClass)) 198 cananian 1.1.2.1 it.remove(); 199 cananian 1.1.2.1 // add to all out-sets 200 cananian 1.1.2.1 for (int i=0; i<q.nextLength(); i++) { 201 cananian 1.1.2.1 boolean changed = false; 202 cananian 1.1.2.1 Edge out = q.nextEdge(i); 203 cananian 1.1.2.1 EdgeProp nep = (EdgeProp) results.get(aliasMap.find(out)); 204 cananian 1.1.2.1 if (nep==null) { 205 cananian 1.1.2.1 nep = new EdgeProp(sf.makeSet(oep.reads), (Quad)out.to()); 206 cananian 1.1.2.1 results.put(aliasMap.find(out), nep); 207 cananian 1.1.2.1 changed=true; 208 cananian 1.1.2.1 } 209 cananian 1.1.2.1 if (nep.reads.addAll(mayRead)) 210 cananian 1.1.2.1 changed=true; 211 cananian 1.1.2.1 if (changed) 212 cananian 1.1.2.1 W.add(nep.next); 213 cananian 1.1.2.1 } 214 cananian 1.1.2.1 } 215 cananian 1.1.2.1 // we could refactor both the PHI and SIGMA *and* one-in-one-out 216 cananian 1.1.2.1 // non-GET cases together into one method, but I think it's 217 cananian 1.1.2.1 // easier to read if we keep them a *little* separate, at least. 218 cananian 1.1.2.1 // I *did* combine PHI and SIGMA into the following method, 219 cananian 1.1.2.1 // which deals with both. all out-sets get all in-sets added 220 cananian 1.1.2.1 // to them. 221 cananian 1.1.2.1 void visitPhiSigma(Quad q) { 222 cananian 1.1.2.1 // for all 'out' edges... 223 cananian 1.1.2.1 for (int i=0; i<q.nextLength(); i++) { 224 cananian 1.1.2.1 boolean changed=false; 225 cananian 1.1.2.1 // get current out-set. 226 cananian 1.1.2.1 Edge out = q.nextEdge(i); 227 cananian 1.1.2.1 EdgeProp nep = (EdgeProp) results.get(aliasMap.find(out)); 228 cananian 1.1.2.1 // merge over all 'in' edges... 229 cananian 1.1.2.1 for (int j=0; j<q.prevLength(); j++) { 230 cananian 1.1.2.1 // get current in-set. 231 cananian 1.1.2.1 Edge in = q.prevEdge(j); 232 cananian 1.1.2.1 EdgeProp oep = (EdgeProp) results.get(aliasMap.find(in)); 233 cananian 1.1.2.1 if (oep==null) { 234 cananian 1.3.2.1 assert q.prevLength()>1; // only for PHIs. 235 cananian 1.1.2.1 continue; // skip this edge; nothing yet known. 236 cananian 1.1.2.1 } 237 cananian 1.1.2.1 if (nep==null) { 238 cananian 1.1.2.1 // if we don't have an out-set yet, create one. 239 cananian 1.1.2.1 // the one-in-one-out non-quad special case 240 cananian 1.1.2.1 // would go in here. but we're going to keep 241 cananian 1.1.2.1 // this clean. 242 cananian 1.1.2.1 changed=true; 243 cananian 1.1.2.1 // (new out set with 'reads' of the in-set. 244 cananian 1.1.2.1 nep = new EdgeProp(sf.makeSet(oep.reads), 245 cananian 1.1.2.1 (Quad) out.to()); 246 cananian 1.1.2.1 results.put(aliasMap.find(out), nep); 247 cananian 1.1.2.1 } else if (nep.reads.addAll(oep.reads)) 248 cananian 1.1.2.1 // we added stuff to the out-set. 249 cananian 1.1.2.1 changed = true; 250 cananian 1.1.2.1 } 251 cananian 1.1.2.1 // if anything changed, add our successor to the worklist. 252 cananian 1.1.2.1 if (changed) 253 cananian 1.1.2.1 W.add(nep.next); 254 cananian 1.1.2.1 } 255 cananian 1.1.2.1 } 256 cananian 1.1.2.1 public void visit(PHI q) { visitPhiSigma(q); } 257 cananian 1.1.2.1 public void visit(SIGMA q) { visitPhiSigma(q); } 258 cananian 1.1.2.1 259 cananian 1.1.2.1 // RETURN and THROW are the end of the line. no out-set. 260 cananian 1.1.2.1 public void visit(RETURN q) { /* do nothing */ } 261 cananian 1.1.2.1 public void visit(THROW q) { /* do nothing */ } 262 cananian 1.1.2.1 } 263 cananian 1.1.2.1 264 cananian 1.1.2.3 /** return a conservative approximation to whether this is a constructor 265 cananian 1.1.2.3 * or not. it's always safe to return true. */ 266 cananian 1.1.2.3 private static boolean isConstructor(HMethod hm) { 267 cananian 1.1.2.3 // this is tricky, because we want split constructors to count, too, 268 cananian 1.1.2.3 // even though renamed constructors (such as generated by initcheck, 269 cananian 1.1.2.3 // for instance) won't always be instanceof HConstructor. Look 270 cananian 1.1.2.3 // for names starting with '<init>', as well. 271 cananian 1.1.2.3 if (hm instanceof HConstructor) return true; 272 cananian 1.1.2.3 if (hm.getName().startsWith("<init>")) return true; 273 cananian 1.1.2.3 // XXX: what about methods generated by RuntimeMethod Cloner? 274 cananian 1.1.2.3 // we could try methods ending with <init>, but then the 275 cananian 1.1.2.3 // declaringclass information would be wrong. 276 cananian 1.1.2.3 //if (hm.getName().endsWidth("<init>")) return true;//not safe yet. 277 cananian 1.1.2.3 return false; 278 cananian 1.1.2.3 } 279 cananian 1.2 }