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      }