1 cananian 1.1.2.1 // MethodSplitter.java, created Thu Oct 5 15:48:13 2000 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.Transformation; 5 cananian 1.1.2.1 6 cananian 1.1.2.10 import harpoon.Analysis.ClassHierarchy; 7 cananian 1.1.2.11 import harpoon.Backend.Maps.FinalMap; 8 cananian 1.1.2.5 import harpoon.ClassFile.CachingCodeFactory; 9 cananian 1.1.2.5 import harpoon.ClassFile.DuplicateMemberException; 10 cananian 1.1.2.5 import harpoon.ClassFile.HClass; 11 cananian 1.1.2.5 import harpoon.ClassFile.HClassMutator; 12 cananian 1.1.2.5 import harpoon.ClassFile.HCode; 13 cananian 1.1.2.5 import harpoon.ClassFile.HCodeAndMaps; 14 cananian 1.1.2.5 import harpoon.ClassFile.HCodeFactory; 15 cananian 1.1.2.10 import harpoon.ClassFile.HConstructor; 16 cananian 1.1.2.5 import harpoon.ClassFile.HMethod; 17 cananian 1.1.2.7 import harpoon.ClassFile.SerializableCodeFactory; 18 cananian 1.7 import net.cscott.jutil.Default; 19 cananian 1.1.2.5 import harpoon.Util.Util; 20 cananian 1.7 import net.cscott.jutil.WorkSet; 21 cananian 1.1.2.1 22 cananian 1.1.2.10 import java.lang.reflect.Modifier; 23 cananian 1.1.2.5 import java.util.HashMap; 24 cananian 1.1.2.13 import java.util.Iterator; 25 cananian 1.1.2.9 import java.util.List; 26 cananian 1.1.2.5 import java.util.Map; 27 cananian 1.1.2.1 /** 28 cananian 1.1.2.1 * <code>MethodSplitter</code> makes it easier to implement 29 cananian 1.1.2.1 * transformations which specialize methods for one purpose or 30 cananian 1.1.2.2 * another. It is meant to be subclassed. In your subclass, 31 cananian 1.1.2.2 * you will likely want to create a few static fields of type 32 cananian 1.1.2.2 * <code>MethodSplitter.Token</code> to name your specialized 33 cananian 1.1.2.2 * versions, override the <code>isValidToken()</code> method 34 cananian 1.1.2.2 * to include your new tokens, and override 35 cananian 1.1.2.2 * <code>mutateDescriptor()</code> and/or <code>mutateHCode()</code> 36 cananian 1.1.2.2 * to effect the specialization. 37 cananian 1.1.2.7 * <p> 38 cananian 1.1.2.7 * Note that if you mutate the <code>ORIGINAL</code> version of 39 cananian 1.1.2.7 * a method, all split versions will inherit the mutation. 40 cananian 1.1.2.7 * Be careful not to introduce cycles because of this ordering. 41 cananian 1.1.2.1 * 42 cananian 1.1.2.1 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 43 cananian 1.7 * @version $Id: MethodSplitter.java,v 1.7 2004/02/08 01:54:24 cananian Exp $ 44 cananian 1.1.2.1 */ 45 cananian 1.1.2.16 public abstract class MethodSplitter implements java.io.Serializable { 46 cananian 1.1.2.2 /** The <code>ORIGINAL</code> token represents the original pre-split 47 cananian 1.1.2.2 * version of a method. */ 48 cananian 1.1.2.16 public static final Token ORIGINAL = new Token(null) { 49 cananian 1.1.2.16 public Object readResolve() { return ORIGINAL; } 50 cananian 1.1.2.16 public String toString() { return "TOKEN<ORIGINAL>"; } 51 cananian 1.1.2.16 }; 52 cananian 1.1.2.2 /** This is the code factory which contains the representations of the 53 cananian 1.1.2.2 * new split methods. */ 54 cananian 1.1.2.2 private final CachingCodeFactory hcf; 55 cananian 1.1.2.10 /** This is a class hierarchy, needed to properly split virtual methods. 56 cananian 1.1.2.10 * (Virtual methods have to be also split in all subclasses, in order 57 cananian 1.1.2.10 * for dynamic dispatch to work correctly.) */ 58 cananian 1.1.2.10 private final ClassHierarchy ch; 59 cananian 1.1.2.11 /** This is the FinalMap used in the implementation of isVirtual(). */ 60 cananian 1.1.2.11 private final FinalMap fm; 61 cananian 1.1.2.2 62 cananian 1.1.2.2 /** Creates a <code>MethodSplitter</code>, based on the method 63 cananian 1.1.2.2 * representations in the <code>parent</code> <code>HCodeFactory</code>. 64 cananian 1.1.2.18 * @param mutateOriginalBeforeSplit if <code>true</code>, the 65 cananian 1.1.2.18 * <i>mutated</i> version of the original is cloned and given 66 cananian 1.1.2.18 * to <code>mutateHCode</code> as source for a split methods; 67 cananian 1.1.2.18 * otherwise, the <i>unmutated</i> version of the original is 68 cananian 1.1.2.18 * taken as the source for the split method. If it doesn't 69 cananian 1.1.2.18 * matter, choose <code>true</code>, as this reduces the 70 cananian 1.1.2.18 * memory footprint. 71 cananian 1.1.2.2 */ 72 cananian 1.1.2.22 public MethodSplitter(final HCodeFactory _parent, ClassHierarchy ch, 73 cananian 1.1.2.18 final boolean mutateOriginalBeforeSplit) { 74 cananian 1.1.2.18 final Map origcache = //this is cache for unmutated version 75 cananian 1.1.2.18 mutateOriginalBeforeSplit ? null : new HashMap(); 76 cananian 1.1.2.7 this.hcf = new CachingCodeFactory(new SerializableCodeFactory() { 77 cananian 1.1.2.19 public String getCodeName() { 78 cananian 1.1.2.22 return mutateCodeName(_parent.getCodeName()); 79 cananian 1.1.2.19 } 80 cananian 1.1.2.22 public void clear(HMethod m) { _parent.clear(m); } 81 cananian 1.1.2.7 public HCode convert(HMethod m) { 82 cananian 1.1.2.9 List swpair = (List) split2orig.get(m); 83 cananian 1.1.2.9 Token tok = (swpair==null) ? ORIGINAL : (Token) swpair.get(1); 84 cananian 1.6 85 cananian 1.6 // For !mutateOriginalBeforeSplit, always do ORIGINAL before 86 cananian 1.6 // split. [Karen found this bug.] 87 cananian 1.6 if (!mutateOriginalBeforeSplit && tok!=ORIGINAL) 88 cananian 1.6 MethodSplitter.this.hcf.convert( (HMethod)swpair.get(0) ); 89 cananian 1.6 90 cananian 1.1.2.22 HCode hc = (tok==ORIGINAL) ? _parent.convert(m) : 91 cananian 1.1.2.22 // get unmutated version from cache... 92 cananian 1.1.2.22 (origcache!=null) ? (HCode)origcache.get( swpair.get(0) ) : 93 cananian 1.1.2.17 hcf.convert( (HMethod)swpair.get(0) ); 94 cananian 1.1.2.22 // put unmutated version in cache 95 cananian 1.1.2.22 if (origcache!=null && tok==ORIGINAL) 96 cananian 1.1.2.22 origcache.put(m, hc); 97 cananian 1.1.2.7 try { 98 cananian 1.1.2.20 if (hc!=null) hc = mutateHCode(cloneHCode(hc, m), tok); 99 cananian 1.1.2.7 } catch (CloneNotSupportedException ex) { 100 cananian 1.3.2.1 assert false : ("cloning HCode failed: "+ex); 101 cananian 1.1.2.7 } 102 cananian 1.1.2.7 return hc; 103 cananian 1.1.2.18 } 104 cananian 1.1.2.18 }, true/* save cache */) { 105 cananian 1.1.2.18 public void clear(HMethod m) { 106 cananian 1.1.2.22 // this version leaks some methods, but it is safe. 107 cananian 1.1.2.22 if (select(m, ORIGINAL).equals(m) && 108 cananian 1.1.2.22 (origcache==null || !origcache.containsKey(m))) 109 cananian 1.1.2.22 parent.clear(m); 110 cananian 1.1.2.22 else 111 cananian 1.1.2.22 super.clear(m); 112 cananian 1.1.2.22 /* 113 cananian 1.1.2.22 //XXX: this doesn't work because we can clear original 114 cananian 1.1.2.22 //method before all its mutated children are created. 115 cananian 1.1.2.18 // top cache responsible for origcache too. 116 cananian 1.1.2.18 super.clear(m); 117 cananian 1.1.2.18 if (origcache!=null) origcache.remove(m); 118 cananian 1.1.2.22 */ 119 cananian 1.1.2.18 } 120 cananian 1.1.2.18 }; 121 cananian 1.1.2.10 this.ch = ch; 122 cananian 1.1.2.12 this.fm = new harpoon.Backend.Maps.CHFinalMap(ch); 123 cananian 1.1.2.1 } 124 cananian 1.1.2.1 125 cananian 1.1.2.9 /** Maps split methods to <original method, token> pairs. */ 126 cananian 1.1.2.1 private final Map split2orig = new HashMap(); 127 cananian 1.1.2.1 /** Maps <original method, token> pairs to created split methods. */ 128 cananian 1.1.2.1 private final Map versions = new HashMap(); 129 cananian 1.1.2.1 130 cananian 1.1.2.1 /** Go from a (possibly already split) method to the version of the 131 cananian 1.1.2.2 * method named by the token <code>which</code>. */ 132 cananian 1.1.2.1 public final synchronized HMethod select(HMethod source, Token which) { 133 cananian 1.3.2.1 assert isValidToken(which) : "token is not valid"; 134 cananian 1.1.2.1 HMethod orig = split2orig.containsKey(source) ? 135 cananian 1.1.2.9 (HMethod) ((List)split2orig.get(source)).get(0) : source; 136 cananian 1.1.2.1 if (which == ORIGINAL) return orig; 137 cananian 1.3.2.1 assert which.suffix!=null : "Null token suffixes are not allowed!"; 138 cananian 1.1.2.15 List swpair = Default.pair(orig, which); 139 cananian 1.1.2.9 HMethod splitM = (HMethod) versions.get(swpair); 140 cananian 1.1.2.1 if (splitM == null) { 141 cananian 1.1.2.1 HClassMutator hcm = orig.getDeclaringClass().getMutator(); 142 cananian 1.3.2.1 assert hcm!=null : "You're using a linker, not a relinker: "+orig+" "+orig.getDeclaringClass().getLinker(); 143 cananian 1.1.2.1 String mname = orig.getName()+"$$"+which.suffix; 144 cananian 1.1.2.1 String mdesc = mutateDescriptor(orig, which); 145 cananian 1.1.2.4 try { 146 cananian 1.1.2.4 splitM = hcm.addDeclaredMethod(mname, mdesc); 147 cananian 1.1.2.4 } catch (DuplicateMemberException dme) { 148 cananian 1.1.2.4 // we can't rename the method, because then inheritance 149 cananian 1.1.2.4 // would not work correctly. 150 cananian 1.3.2.1 assert false : "Can't create method "+mname+mdesc+" in "+ 151 cananian 1.1.2.4 orig.getDeclaringClass()+" because it already "+ 152 cananian 1.3.2.1 "exists"; 153 cananian 1.1.2.1 } 154 cananian 1.1.2.8 splitM.getMutator().setModifiers(orig.getModifiers()); 155 cananian 1.1.2.8 splitM.getMutator().setSynthetic(orig.isSynthetic()); 156 cananian 1.1.2.21 // XXX: we currently can't add "renamed" constructors, so 157 cananian 1.1.2.21 // if we're splitting a constructor make it private to 158 cananian 1.1.2.21 // ensure that it is non-virtual. 159 cananian 1.1.2.21 if (orig instanceof HConstructor) 160 cananian 1.1.2.21 splitM.getMutator().addModifiers(Modifier.PRIVATE); 161 cananian 1.1.2.1 /* now add this to known versions */ 162 cananian 1.1.2.9 versions.put(swpair, splitM); 163 cananian 1.1.2.9 split2orig.put(splitM, swpair); 164 cananian 1.1.2.10 /* now split all subclasses */ 165 cananian 1.1.2.13 if (isVirtual(orig)) //only keep splitting if orig is inheritable 166 cananian 1.1.2.13 for (Iterator it=ch.overrides(orig).iterator(); it.hasNext(); ) 167 cananian 1.1.2.13 // XXX: a little conservative, since select() will then 168 cananian 1.1.2.13 // turn around and split all the child's children. 169 cananian 1.1.2.13 // ch.overrides(orig, ..., true) would give more accurate 170 cananian 1.1.2.13 // results. 171 cananian 1.1.2.13 select((HMethod)it.next(), which); 172 cananian 1.1.2.9 /* done */ 173 cananian 1.1.2.1 } 174 cananian 1.1.2.1 return splitM; 175 cananian 1.1.2.1 } 176 cananian 1.1.2.10 /** Utility method to determine whether a method is inheritable (and 177 cananian 1.1.2.10 * thus it's children should be split whenever it is). */ 178 cananian 1.1.2.11 protected boolean isVirtual(HMethod m) { 179 cananian 1.1.2.10 if (m.isStatic()) return false; 180 cananian 1.1.2.10 if (Modifier.isPrivate(m.getModifiers())) return false; 181 cananian 1.1.2.10 if (m instanceof HConstructor) return false; 182 cananian 1.5 if (!m.getDeclaringClass().isArray()) 183 cananian 1.5 // XXX arrays are somewhat misleadingly classified as 'final' 184 cananian 1.5 // even though we provide for inheritance of clone() methods 185 cananian 1.5 // and such between array classes. =( 186 cananian 1.5 // (this is the fruit of the strange discrepancies in array 187 cananian 1.5 // inheritance. does A[] derive from Object[] or from Object?) 188 cananian 1.5 if (fm.isFinal(m)) return false; 189 cananian 1.1.2.10 return true; 190 cananian 1.1.2.10 } 191 cananian 1.1.2.10 192 cananian 1.1.2.2 /** Returns a <code>HCodeFactory</code> containing representations for 193 cananian 1.1.2.2 * the methods split by the <code>MethodSplitter</code>. */ 194 cananian 1.1.2.12 public HCodeFactory codeFactory() { return hcf; } 195 cananian 1.1.2.1 196 cananian 1.1.2.1 /** Override this method if you want to create mutated methods 197 cananian 1.1.2.1 * with descriptors differing from that of the original method. 198 cananian 1.1.2.1 */ 199 cananian 1.1.2.1 protected String mutateDescriptor(HMethod hm, Token which) { 200 cananian 1.1.2.1 return hm.getDescriptor(); 201 cananian 1.1.2.1 } 202 cananian 1.1.2.2 /** Override this method to effect transformations on split 203 cananian 1.1.2.2 * methods. */ 204 cananian 1.1.2.3 protected HCode mutateHCode(HCodeAndMaps input, Token which) { 205 cananian 1.1.2.3 return input.hcode(); 206 cananian 1.1.2.19 } 207 cananian 1.1.2.19 /** Override this method to change the codename which this 208 cananian 1.1.2.19 * <code>MethodMutator</code>'s codefactory reports. */ 209 cananian 1.1.2.19 protected String mutateCodeName(String codeName) { 210 cananian 1.1.2.19 return codeName; 211 cananian 1.1.2.20 } 212 cananian 1.1.2.20 /** Override this method if you do not want the mutatable HCode to be 213 cananian 1.1.2.20 * a straight clone of the original HCode: for example, if the 214 cananian 1.1.2.20 * input HCodes were <code>QuadSSI</code> and you wanted to 215 cananian 1.1.2.20 * clone them into <code>QuadRSSI</code>s before mutating. 216 cananian 1.1.2.20 * By default, this method returns <code>hc.clone(newmethod)</code>. */ 217 cananian 1.1.2.20 protected HCodeAndMaps cloneHCode(HCode hc, HMethod newmethod) 218 cananian 1.1.2.20 throws CloneNotSupportedException { 219 cananian 1.1.2.20 return hc.clone(newmethod); 220 cananian 1.1.2.2 } 221 cananian 1.1.2.2 222 cananian 1.1.2.1 /** Check the validity of a given <code>MethodSplitter.Token</code>. 223 cananian 1.1.2.1 * Override if (when) your subclass defines new tokens. */ 224 cananian 1.1.2.1 protected boolean isValidToken(Token which) { 225 cananian 1.1.2.1 return which==ORIGINAL; 226 cananian 1.1.2.1 } 227 cananian 1.1.2.1 228 cananian 1.1.2.2 /** Subclasses of <code>MethodSplitter</code> refer to "versions" 229 cananian 1.1.2.2 * of the underlying method which may be named by creating 230 cananian 1.1.2.1 * static instances of this <code>MethodSplitter.Token</code> 231 cananian 1.1.2.1 * class. The argument to the constructor specifies a default 232 cananian 1.1.2.2 * suffix for the newly-split method's name. Don't forget 233 cananian 1.1.2.2 * to extend <code>MethodSplitter.isValidToken()</code> to 234 cananian 1.1.2.16 * include your new <code>MethodSplitter.Token</code> subclasses. 235 cananian 1.1.2.16 * <p> 236 cananian 1.1.2.16 * A typical subclass of <code>MethodSplitter</code> will include 237 cananian 1.1.2.16 * the following code fragment:<p> 238 cananian 1.1.2.16 * <pre> 239 cananian 1.1.2.16 * public class FooBlah extends <code>MethodSplitter</code> { 240 cananian 1.1.2.16 * /** Token for the foo-blah version of a method. *<b></b>/ 241 cananian 1.1.2.16 * public static final Token FOOBLAH = new Token("fooblah") { 242 cananian 1.1.2.16 * /** This ensures that FOOBLAH is a singleton object. *<b></b>/ 243 cananian 1.1.2.16 * public Object readResolve() { return FOOBLAH; } 244 cananian 1.1.2.16 * }; 245 cananian 1.1.2.16 * /** Checks the token types handled by this 246 cananian 1.1.2.16 * * <code>MethodSplitter</code> subclass. *<b></b>/ 247 cananian 1.1.2.16 * protected boolean isValidToken(Token which) { 248 cananian 1.1.2.16 * return which==FOOBLAH || super.isValidToken(which); 249 cananian 1.1.2.16 * }; 250 cananian 1.1.2.16 * }; 251 cananian 1.1.2.16 * </pre> 252 cananian 1.1.2.16 */ 253 cananian 1.1.2.16 protected abstract static class Token implements java.io.Serializable { 254 cananian 1.1.2.1 final String suffix; 255 cananian 1.1.2.16 /** Create a token, specifying the suggested method suffix. */ 256 cananian 1.1.2.16 protected Token(String suggestedSuffix) { 257 cananian 1.1.2.1 this.suffix = suggestedSuffix; 258 cananian 1.1.2.1 } 259 cananian 1.1.2.16 /** This method must be overridden to ensure that <code>Token</code>s 260 cananian 1.1.2.16 * are still singletons after deserialization. See the template 261 cananian 1.1.2.16 * in the class description above. */ 262 cananian 1.1.2.16 protected abstract Object readResolve(); 263 cananian 1.1.2.16 /** Returns a human-readable representation of this token. */ 264 cananian 1.1.2.16 public String toString() { return "TOKEN["+suffix+"]"; } 265 cananian 1.1.2.1 } 266 cananian 1.2 }