1 cananian 1.1.2.1 // DefiniteInitOracle.java, created Mon Nov 5 16:14:29 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.Analysis.ClassHierarchy; 7 cananian 1.1.2.1 import harpoon.Analysis.DomTree; 8 cananian 1.1.2.1 import harpoon.ClassFile.HClass; 9 cananian 1.1.2.1 import harpoon.ClassFile.HCode; 10 cananian 1.1.2.1 import harpoon.ClassFile.HCodeElement; 11 cananian 1.1.2.1 import harpoon.ClassFile.HCodeFactory; 12 cananian 1.1.2.1 import harpoon.ClassFile.HConstructor; 13 cananian 1.1.2.1 import harpoon.ClassFile.HField; 14 cananian 1.1.2.1 import harpoon.ClassFile.HMethod; 15 cananian 1.1.2.1 import harpoon.IR.Quads.CALL; 16 cananian 1.1.2.5 import harpoon.IR.Quads.Edge; 17 cananian 1.1.2.1 import harpoon.IR.Quads.METHOD; 18 cananian 1.1.2.1 import harpoon.IR.Quads.MOVE; 19 cananian 1.1.2.1 import harpoon.IR.Quads.PHI; 20 cananian 1.1.2.1 import harpoon.IR.Quads.Quad; 21 cananian 1.1.2.5 import harpoon.IR.Quads.QuadFactory; 22 cananian 1.1.2.1 import harpoon.IR.Quads.QuadVisitor; 23 cananian 1.1.2.1 import harpoon.IR.Quads.RETURN; 24 cananian 1.1.2.1 import harpoon.IR.Quads.SET; 25 cananian 1.1.2.1 import harpoon.IR.Quads.SIGMA; 26 cananian 1.5 import net.cscott.jutil.AggregateSetFactory; 27 cananian 1.5 import net.cscott.jutil.GenericMultiMap; 28 cananian 1.5 import net.cscott.jutil.MultiMap; 29 cananian 1.1.2.6 import harpoon.Util.Util; 30 cananian 1.1.2.1 31 cananian 1.1.2.1 import java.util.Arrays; 32 cananian 1.1.2.1 import java.util.Collections; 33 cananian 1.1.2.3 import java.util.HashMap; 34 cananian 1.1.2.1 import java.util.HashSet; 35 cananian 1.1.2.1 import java.util.Iterator; 36 cananian 1.1.2.1 import java.util.List; 37 cananian 1.1.2.3 import java.util.Map; 38 cananian 1.1.2.1 import java.util.Set; 39 cananian 1.1.2.1 40 cananian 1.1.2.1 /** 41 cananian 1.1.2.1 * A <code>DefiniteInitOracle</code> tells you whether a given 42 cananian 1.1.2.1 * field is "definitely initialized" before control flow leaves 43 cananian 1.1.2.5 * every constructor of its declaring class. A field is not 44 cananian 1.1.2.5 * considered to be "definitely initialized" if it "may" be read 45 cananian 1.1.2.5 * before its definite initialization point. 46 cananian 1.1.2.1 * 47 cananian 1.1.2.1 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 48 cananian 1.6 * @version $Id: DefiniteInitOracle.java,v 1.6 2004/02/08 03:20:10 cananian Exp $ 49 cananian 1.1.2.1 */ 50 cananian 1.1.2.1 public class DefiniteInitOracle { 51 cananian 1.1.2.5 private static final boolean DEBUG=true; 52 cananian 1.1.2.3 final HCodeFactory hcf; 53 cananian 1.1.2.1 final ClassHierarchy ch; 54 cananian 1.1.2.1 final Set notDefinitelyInitialized = new HashSet(); 55 cananian 1.1.2.1 56 cananian 1.1.2.1 /** Queries the <code>DefiniteInitOracle</code>; returns <code>true</code> 57 cananian 1.1.2.1 * iff every constructor for the declaring class of <code>hf</code> 58 cananian 1.1.2.1 * *must* define the field before any reference to the field is made. 59 cananian 1.1.2.1 */ 60 cananian 1.1.2.1 public boolean isDefinitelyInitialized(HField hf) { 61 cananian 1.1.2.1 if (hf.isStatic()) return false; // XXX: would need to look at <clinit> 62 cananian 1.1.2.1 if (!ch.instantiatedClasses().contains(hf.getDeclaringClass())) 63 cananian 1.1.2.1 return false; // never instantiated; thus not def. initialized. 64 cananian 1.1.2.1 return !notDefinitelyInitialized.contains(hf); 65 cananian 1.1.2.1 } 66 cananian 1.1.2.1 /** Creates a <code>DefiniteInitOracle</code> which will use 67 cananian 1.1.2.1 * the given code factory and hierarchy approximation. For best 68 cananian 1.1.2.1 * results, the code factory should produce SSI or SSA form. */ 69 cananian 1.1.2.1 public DefiniteInitOracle(HCodeFactory hcf, ClassHierarchy ch) { 70 cananian 1.1.2.3 this.hcf = hcf; 71 cananian 1.1.2.1 this.ch = ch; 72 cananian 1.1.2.5 this.cg = new CallGraphImpl(ch, hcf); 73 cananian 1.1.2.5 this.fso = new FieldSyncOracle(hcf, ch, cg); 74 cananian 1.1.2.1 // for each constructor: 75 cananian 1.6 for (Object hmO : ch.callableMethods()) { 76 cananian 1.6 HMethod hm = (HMethod) hmO; 77 cananian 1.1.2.2 if (!isConstructor(hm)) continue; 78 cananian 1.1.2.3 Set di = getNotDefInit(hm); 79 cananian 1.1.2.3 notDefinitelyInitialized.addAll(di); 80 cananian 1.1.2.3 } 81 cananian 1.1.2.4 // free memory. 82 cananian 1.1.2.4 fso=null; 83 cananian 1.1.2.5 cg=null; 84 cananian 1.1.2.4 ndiCache=null; 85 cananian 1.1.2.3 // done! 86 cananian 1.1.2.5 if (DEBUG) { // debugging: 87 cananian 1.1.2.5 System.out.println("DEFINITELY INITIALIZED FIELDS:"); 88 cananian 1.6 for (Object hcO : ch.classes()) { 89 cananian 1.6 HClass hc = (HClass) hcO; 90 cananian 1.6 for (Object hfO : hc.getDeclaredFields()) { 91 cananian 1.6 HField hf = (HField) hfO; 92 cananian 1.1.2.5 if (isDefinitelyInitialized(hf)) 93 cananian 1.1.2.5 System.out.println(" "+hf); 94 cananian 1.1.2.5 } 95 cananian 1.1.2.5 } 96 cananian 1.1.2.5 System.out.println("------------------------------"); 97 cananian 1.1.2.5 } 98 cananian 1.1.2.3 } 99 cananian 1.1.2.4 100 cananian 1.1.2.4 // only used during analysis; clear afterwards 101 cananian 1.1.2.4 FieldSyncOracle fso; 102 cananian 1.1.2.5 CallGraph cg; 103 cananian 1.1.2.4 Map ndiCache = new HashMap(); 104 cananian 1.1.2.4 105 cananian 1.1.2.3 Set getNotDefInit(HMethod hm) { 106 cananian 1.1.2.3 if (!ndiCache.containsKey(hm)) { 107 cananian 1.1.2.3 Set notDefinitelyInitialized = new HashSet(); 108 cananian 1.1.2.1 HCode hc = hcf.convert(hm); 109 cananian 1.3.2.1 assert hc!=null : hm; 110 cananian 1.1.2.1 // compute dominator tree. 111 cananian 1.1.2.1 DomTree dt = new DomTree(hc, false); 112 cananian 1.1.2.5 // compute MayReadOracle 113 cananian 1.1.2.5 MayReadOracle mro = new MayReadOracle(hc, fso, cg, 114 cananian 1.1.2.8 hm.getDeclaringClass(), 115 cananian 1.1.2.8 true/*special handling!*/); 116 cananian 1.1.2.6 // compute MustParamOracle (to find variables which must contain 117 cananian 1.1.2.6 // 'this') 118 cananian 1.1.2.6 MustParamOracle mpo = new MustParamOracle(hc); 119 cananian 1.1.2.1 // make analysis results cache. 120 cananian 1.1.2.1 MultiMap cache = new GenericMultiMap(new AggregateSetFactory()); 121 cananian 1.1.2.1 // for each exit point: 122 cananian 1.1.2.5 for (Iterator it2=findExitPoints(hc).iterator(); it2.hasNext(); ) { 123 cananian 1.1.2.1 // recursively determine set of 'definitely initialized' 124 cananian 1.1.2.1 // fields. 125 cananian 1.1.2.1 Set definit = findDefInit((HCodeElement)it2.next(), 126 cananian 1.1.2.6 dt, mpo, mro, cache); 127 cananian 1.1.2.1 // add any fields which are *not* definitely initialized to 128 cananian 1.1.2.1 // our set. 129 cananian 1.6 for (Object hfO : hm.getDeclaringClass().getDeclaredFields()) { 130 cananian 1.6 HField hf = (HField) hfO; 131 cananian 1.1.2.1 if (hf.isStatic()) continue; 132 cananian 1.1.2.1 if (definit.contains(hf)) continue; // definitely init'd 133 cananian 1.1.2.1 // not definitely init'd! 134 cananian 1.1.2.1 notDefinitelyInitialized.add(hf); 135 cananian 1.1.2.1 } 136 cananian 1.1.2.1 } 137 cananian 1.1.2.3 // okay, cache this! 138 cananian 1.1.2.3 ndiCache.put 139 cananian 1.1.2.3 (hm, Collections.unmodifiableSet(notDefinitelyInitialized)); 140 cananian 1.1.2.1 } 141 cananian 1.1.2.3 return (Set) ndiCache.get(hm); 142 cananian 1.1.2.1 } 143 cananian 1.1.2.2 /** return a conservative approximation to whether this is a constructor 144 cananian 1.1.2.2 * or not. it's always safe to return true. */ 145 cananian 1.1.2.2 boolean isConstructor(HMethod hm) { 146 cananian 1.1.2.2 // this is tricky, because we want split constructors to count, too, 147 cananian 1.1.2.2 // even though renamed constructors (such as generated by initcheck, 148 cananian 1.1.2.2 // for instance) won't always be instanceof HConstructor. Look 149 cananian 1.1.2.2 // for names starting with '<init>', as well. 150 cananian 1.1.2.2 if (hm instanceof HConstructor) return true; 151 cananian 1.1.2.2 if (hm.getName().startsWith("<init>")) return true; 152 cananian 1.1.2.2 // XXX: what about methods generated by RuntimeMethod Cloner? 153 cananian 1.1.2.2 // we could try methods ending with <init>, but then the 154 cananian 1.1.2.2 // declaringclass information would be wrong. 155 cananian 1.1.2.2 //if (hm.getName().endsWidth("<init>")) return true;//not safe yet. 156 cananian 1.1.2.2 return false; 157 cananian 1.1.2.2 } 158 cananian 1.1.2.3 /** Is this a 'this' constructor? Safe to return false if unsure. */ 159 cananian 1.1.2.3 boolean isThisConstructor(HMethod hm, HCodeElement me) { 160 cananian 1.1.2.3 return isConstructor(hm) && // assumes this method is precise. 161 cananian 1.1.2.3 hm.getDeclaringClass().equals 162 cananian 1.1.2.3 (((Quad)me).getFactory().getMethod().getDeclaringClass()); 163 cananian 1.1.2.3 } 164 cananian 1.1.2.3 /** Is this a super constructor? Safe to return false if unsure. */ 165 cananian 1.1.2.3 boolean isSuperConstructor(HMethod hm, HCodeElement me) { 166 cananian 1.1.2.3 return isConstructor(hm) && // assumes this method is precise. 167 cananian 1.1.2.3 hm.getDeclaringClass().equals 168 cananian 1.1.2.3 (((Quad)me).getFactory().getMethod().getDeclaringClass() 169 cananian 1.1.2.3 .getSuperclass()); 170 cananian 1.1.2.3 } 171 cananian 1.1.2.1 172 cananian 1.1.2.5 Set findExitPoints(HCode hc) { 173 cananian 1.1.2.5 // exit points are returns. 174 cananian 1.1.2.5 Set exits = new HashSet(); 175 cananian 1.1.2.5 for (Iterator it=hc.getElementsI(); it.hasNext(); ) { 176 cananian 1.1.2.5 Quad q = (Quad) it.next(); 177 cananian 1.1.2.5 if (q instanceof RETURN) 178 cananian 1.1.2.5 exits.add(q); 179 cananian 1.1.2.4 } 180 cananian 1.1.2.5 return Collections.unmodifiableSet(exits); 181 cananian 1.1.2.1 } 182 cananian 1.1.2.1 Set findDefInit(HCodeElement exit, 183 cananian 1.1.2.6 DomTree dt, MustParamOracle mpo, MayReadOracle mro, 184 cananian 1.1.2.5 MultiMap cache) { 185 cananian 1.1.2.1 return Collections.unmodifiableSet 186 cananian 1.1.2.6 (getDefBefore(exit, dt, mpo, mro, cache)); 187 cananian 1.1.2.1 } 188 cananian 1.1.2.1 private Set getDefBefore(HCodeElement hce, 189 cananian 1.1.2.6 DomTree dt, MustParamOracle mpo, 190 cananian 1.1.2.6 MayReadOracle mro, MultiMap cache) { 191 cananian 1.1.2.1 if (!cache.containsKey(hce)) { 192 cananian 1.1.2.1 // get immediate dominator. 193 cananian 1.1.2.1 HCodeElement idom = dt.idom(hce); 194 cananian 1.1.2.1 if (idom!=null) { // if this *has* an immediate dominator... 195 cananian 1.1.2.1 // get *its* definite initializations. 196 cananian 1.1.2.6 Set definit = getDefBefore(idom, dt, mpo, mro, cache); 197 cananian 1.1.2.1 // all of these are also definitely initialized at this point. 198 cananian 1.1.2.1 cache.addAll(hce, definit); 199 cananian 1.1.2.5 // collect set of reads before this quad. 200 cananian 1.1.2.5 Set readBefore = new HashSet(); 201 cananian 1.1.2.5 Edge[] prevEdges = ((Quad)hce).prevEdge(); 202 cananian 1.1.2.5 for (int i=0; i < prevEdges.length; i++) 203 cananian 1.1.2.5 readBefore.addAll(mro.mayReadAt(prevEdges[i])); 204 cananian 1.1.2.1 // is the idom itself an initialization? 205 cananian 1.1.2.1 if (idom instanceof SET) { 206 cananian 1.1.2.1 SET q = (SET) idom; 207 cananian 1.1.2.1 if (q.objectref()!=null && 208 cananian 1.1.2.6 mpo.isMustParam(q.objectref()) && 209 cananian 1.1.2.6 mpo.whichMustParam(q.objectref())==0/*'this'*/) { 210 cananian 1.1.2.5 // not a definite init if this field can be read before 211 cananian 1.1.2.5 if (!readBefore.contains(q.field())) 212 cananian 1.1.2.5 // baby! add this to the definitely-init'd set! 213 cananian 1.1.2.5 cache.add(hce, q.field()); 214 cananian 1.1.2.3 } 215 cananian 1.1.2.3 } else if (idom instanceof CALL) { 216 cananian 1.1.2.3 CALL q = (CALL) idom; 217 cananian 1.1.2.3 // could this be a 'this' constructor? 218 cananian 1.1.2.3 if (isThisConstructor(q.method(), q)) { 219 cananian 1.1.2.5 // add all the definitely-initialized vars which 220 cananian 1.1.2.5 // have not been read-before. 221 cananian 1.1.2.3 Set ndi = getNotDefInit(q.method()); 222 cananian 1.1.2.3 HClass hc = q.method().getDeclaringClass(); // 'this' 223 cananian 1.6 for (Object hfO : hc.getDeclaredFields()) { 224 cananian 1.6 HField hf = (HField) hfO; 225 cananian 1.1.2.3 if (hf.isStatic()) continue; 226 cananian 1.1.2.3 if (ndi.contains(hf)) continue; // not def init'd 227 cananian 1.1.2.5 if (!readBefore.contains(hf)) 228 cananian 1.1.2.5 cache.add(hce, hf); // def init'd! 229 cananian 1.1.2.3 } 230 cananian 1.1.2.1 } 231 cananian 1.1.2.1 } 232 cananian 1.1.2.1 } 233 cananian 1.1.2.1 // done. 234 cananian 1.1.2.1 } 235 cananian 1.1.2.1 return (Set) cache.getValues(hce); 236 cananian 1.1.2.1 } 237 cananian 1.2 }