1 cananian 1.1.4.1  // Relinker.java, created Mon Dec 27 19:05:58 1999 by cananian
  2 cananian 1.1.4.1  // Copyright (C) 1999 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1.4.1  // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1.4.1  package harpoon.ClassFile;
  5 cananian 1.1.4.1  
  6 cananian 1.1.4.8  import java.util.ArrayList;
  7 cananian 1.1.4.1  import java.util.HashMap;
  8 cananian 1.1.4.8  import java.util.Iterator;
  9 cananian 1.1.4.8  import java.util.List;
 10 cananian 1.1.4.1  import java.util.Map;
 11 cananian 1.5      
 12 cananian 1.5      import net.cscott.jutil.Util;
 13 cananian 1.1.4.1  /**
 14 cananian 1.1.4.1   * A <code>Relinker</code> object is a <code>Linker</code> where one
 15 cananian 1.1.4.1   * can globally replace references to a certain class with references
 16 cananian 1.1.4.1   * to another, different, class.
 17 cananian 1.1.4.1   * 
 18 cananian 1.1.4.1   * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 19 cananian 1.6       * @version $Id: Relinker.java,v 1.6 2004/02/08 05:09:46 cananian Exp $
 20 cananian 1.1.4.1   */
 21 cananian 1.1.4.2  public class Relinker extends Linker implements java.io.Serializable {
 22 cananian 1.1.4.1      protected final Linker linker;
 23 cananian 1.1.4.1  
 24 cananian 1.1.4.1      /** Creates a <code>Relinker</code>. */
 25 cananian 1.1.4.1      public Relinker(Linker linker) {
 26 cananian 1.1.4.1          this.linker = linker;
 27 cananian 1.1.4.1      }
 28 cananian 1.1.4.1      protected HClass forDescriptor0(String descriptor) {
 29 cananian 1.1.4.1          return new HClassProxy(this, linker.forDescriptor(descriptor));
 30 cananian 1.1.4.5      }
 31 cananian 1.1.4.5      protected HClass makeArray(HClass baseType, int dims) {
 32 cananian 1.1.4.10         // two cases: 1) base type's proxy is a mutable class generated
 33 cananian 1.1.4.10         // by this relinker, 2) base type's proxy is from linker.
 34 cananian 1.1.4.10         // proxy base array type in case 2; create our own HClassArray
 35 cananian 1.1.4.10         // for case 1.
 36 cananian 1.1.4.10         if (!baseType.isPrimitive()) {
 37 cananian 1.1.4.10             HClass baseProxy = ((HClassProxy)baseType).proxy;
 38 cananian 1.1.4.10             if (baseProxy.getLinker()==this) // must be our mutable.
 39 cananian 1.1.4.10                 return new HClassProxy
 40 cananian 1.1.4.10                     (this, new HClassArray(this, baseProxy, dims));
 41 cananian 1.1.4.10         }
 42 cananian 1.1.4.10         // otherwise, proxy the linker's array.
 43 cananian 1.1.4.6          String desc = Util.repeatString("[",dims) + baseType.getDescriptor();
 44 cananian 1.1.4.6          return new HClassProxy(this, linker.forDescriptor(desc));
 45 cananian 1.1.4.1      }
 46 cananian 1.1.4.1      
 47 cananian 1.1.4.1      /** Creates a mutable class with the given name which is based on
 48 cananian 1.1.4.1       *  the given template class.  The name <b>need not</b> be unique.
 49 cananian 1.1.4.1       *  If a class with the given name already exists, all references
 50 cananian 1.1.4.1       *  to the existing class are changed to point to the new mutable
 51 cananian 1.1.4.1       *  class returned by this method. */
 52 cananian 1.1.4.1      public HClass createMutableClass(String name, HClass template) {
 53 cananian 1.3.2.1          assert template.getLinker()==this;
 54 cananian 1.1.4.1          HClass newClass = new HClassSyn(this, name, template);
 55 cananian 1.1.4.9          HClass proxyClass = new HClassProxy(this, newClass);
 56 cananian 1.1.4.1          newClass.hasBeenModified=true;
 57 cananian 1.1.4.1          try {
 58 cananian 1.1.4.1              HClass oldClass = forName(name); // get existing proxy class
 59 cananian 1.1.4.1              if (oldClass.equals(template))
 60 cananian 1.1.4.1                  newClass.hasBeenModified=false; // exact copy of oldClass
 61 cananian 1.1.4.9              relink(oldClass, proxyClass);
 62 cananian 1.1.4.1              return oldClass;
 63 cananian 1.1.4.1          } catch (NoSuchClassException e) { // brand spankin' new class
 64 cananian 1.1.4.1              register(proxyClass);
 65 cananian 1.1.4.1              return proxyClass;
 66 cananian 1.1.4.1          }
 67 cananian 1.1.4.1      }
 68 cananian 1.1.4.1  
 69 cananian 1.1.4.1      /** Globally replace all references to <code>oldClass</code> with
 70 cananian 1.1.4.1       *  references to <code>newClass</code>, which may or may not have
 71 cananian 1.1.4.1       *  the same name.  The following constraint must hold:<pre>
 72 cananian 1.1.4.1       *  oldClass.getLinker()==newClass.getLinker()==this
 73 cananian 1.1.4.1       *  </pre><p>
 74 cananian 1.1.4.1       *  <b>WARNING:</b> the <code>hasBeenModified()</code> method of
 75 cananian 1.1.4.1       *  <code>HClass</code>is not reliable after calling 
 76 cananian 1.1.4.1       *  <code>relink()</code> if <code>oldClass.getName()</code> is not the
 77 cananian 1.1.4.1       *  same as <code>newClass.getName()</code>.  The value returned
 78 cananian 1.1.4.1       *  by <code>HClass.hasBeenModified()</code> will not reflect changes
 79 cananian 1.1.4.1       *  due to the global replacement of <code>oldClass</code> with
 80 cananian 1.1.4.1       *  <code>newClass</code> done by this <code>relink()</code>.</p>
 81 cananian 1.1.4.1       */
 82 cananian 1.1.4.1      public void relink(HClass oldClass, HClass newClass) {
 83 cananian 1.1.4.4          // XXX this can cause some .equals() weirdness!
 84 cananian 1.3.2.1          assert oldClass.getLinker()==this;
 85 cananian 1.3.2.1          assert newClass.getLinker()==this;
 86 cananian 1.1.4.1          // we're going to leave the old mapping in, so that classes
 87 cananian 1.1.4.1          // loaded in the future still get the new class.  uncomment
 88 cananian 1.1.4.1          // out the next line if we decide to delete the old descriptor
 89 cananian 1.1.4.1          // mapping when we relink.
 90 cananian 1.1.4.1          //descCache.remove(oldClass.getDescriptor());
 91 cananian 1.1.4.7          ((HClassProxy)oldClass).relink(((HClassProxy)newClass).proxy);
 92 cananian 1.1.4.1          descCache.put(oldClass.getDescriptor(), oldClass);
 93 cananian 1.1.4.1      }
 94 cananian 1.1.4.10 
 95 cananian 1.1.4.10     /** Move <code>HMember</code> <code>hm</code> from its declaring
 96 cananian 1.1.4.10      *  class to some other class, <code>newDestination</code>.  This
 97 cananian 1.1.4.10      *  usually only makes sense if you're moving a member from a
 98 cananian 1.1.4.10      *  class to its superclass, or vice-versa --- but we're not
 99 cananian 1.1.4.10      *  enforcing this (full foot-shooting power granted).  The
100 cananian 1.1.4.10      *  <code>newDestination</code> class must not already have a
101 cananian 1.1.4.10      *  field named the same/method with the same signature as
102 cananian 1.1.4.10      *  <code>hm</code>.  All references to the member in the old class
103 cananian 1.1.4.10      *  will be re-directed to point to the member in the new class,
104 cananian 1.1.4.10      *  and in fact, upon return the given <code>HMember</code>
105 cananian 1.1.4.10      *  <code>hm</code> will refer to the new member.
106 cananian 1.1.4.10      *  <b>WARNING</b>: make sure that any methods which refer to this
107 cananian 1.1.4.10      *  member have been converted to Bytecode form at least *before*
108 cananian 1.1.4.10      *  invoking <code>move()</code>; otherwise field resolution will fail
109 cananian 1.1.4.10      *  when we are parsing the bytecode file (we don't keep any record
110 cananian 1.1.4.10      *  of the 'old' or 'canonical' name of the moved member).
111 cananian 1.1.4.10      */
112 cananian 1.1.4.10     public void move(HMember hm, HClass newDestination)
113 cananian 1.1.4.10         throws DuplicateMemberException {
114 cananian 1.1.4.10         HClass oldDeclarer = hm.getDeclaringClass();
115 cananian 1.3.2.1          assert oldDeclarer.getLinker()==this;
116 cananian 1.3.2.1          assert newDestination.getLinker()==this;
117 cananian 1.3.2.1          assert hm instanceof HMemberProxy;
118 cananian 1.1.4.10         // make sure both old and new classes are mutable.
119 cananian 1.1.4.10         HClassMutator check;
120 cananian 1.3.2.1          check = oldDeclarer.getMutator();       assert check!=null;
121 cananian 1.3.2.1          check = newDestination.getMutator();    assert check!=null;
122 cananian 1.1.4.10         // access mutator of proxied class directly.
123 cananian 1.1.4.10         HClassMutator oldmut = ((HClassProxy)oldDeclarer).proxyMutator;
124 cananian 1.1.4.10         HClassMutator newmut = ((HClassProxy)newDestination).proxyMutator;
125 cananian 1.1.4.10         // add field/method to (proxied) new class
126 cananian 1.1.4.10         HMember newm = (hm instanceof HField) ? (HMember)
127 cananian 1.1.4.10             newmut.addDeclaredField(hm.getName(), (HField)hm) :
128 cananian 1.1.4.10             (hm instanceof HConstructor) ? (HMember)
129 cananian 1.1.4.10             newmut.addConstructor((HConstructor) hm) :
130 cananian 1.1.4.10             (hm instanceof HInitializer) ? (HMember)
131 cananian 1.1.4.10             newmut.addClassInitializer() :
132 cananian 1.1.4.10             (hm instanceof HMethod) ? (HMember)
133 cananian 1.1.4.10             newmut.addDeclaredMethod(hm.getName(), (HMethod)hm) :
134 cananian 1.1.4.10             null/*should never happen!*/;
135 cananian 1.3.2.1          assert newm!=null : "not a field, method, or constructor";
136 cananian 1.1.4.10         // store away original (old) field.
137 cananian 1.1.4.10         HMember oldm = (hm instanceof HField) ? 
138 cananian 1.1.4.10             (HMember) ((HFieldProxy)hm).proxy :
139 cananian 1.1.4.10             (HMember) ((HMethodProxy)hm).proxy;
140 cananian 1.1.4.10         // redirect old field proxy to this new field.
141 cananian 1.1.4.10         int hashcheck = hm.hashCode();
142 cananian 1.1.4.10         if (hm instanceof HField)
143 cananian 1.1.4.10             ((HFieldProxy)hm).relink((HField)newm);
144 cananian 1.1.4.10         else if (hm instanceof HConstructor)
145 cananian 1.1.4.10             ((HConstructorProxy)hm).relink((HConstructor)newm);
146 cananian 1.1.4.10         else if (hm instanceof HInitializer)
147 cananian 1.1.4.10             ((HInitializerProxy)hm).relink((HInitializer)newm);
148 cananian 1.1.4.10         else if (hm instanceof HMethod)
149 cananian 1.1.4.10             ((HMethodProxy)hm).relink((HMethod)newm);
150 cananian 1.3.2.1          else assert false : "not a field, method, or constructor";
151 cananian 1.3.2.1          assert hashcheck==hm.hashCode();// hashcode shouldn't change.
152 cananian 1.1.4.10         // now remove (non-proxied) old field.
153 cananian 1.1.4.10         if (hm instanceof HField)
154 cananian 1.1.4.10             oldmut.removeDeclaredField((HField)oldm);
155 cananian 1.1.4.10         else if (hm instanceof HConstructor)
156 cananian 1.1.4.10             oldmut.removeConstructor((HConstructor)oldm);
157 cananian 1.1.4.10         else if (hm instanceof HInitializer)
158 cananian 1.1.4.10             oldmut.removeClassInitializer((HInitializer)oldm);
159 cananian 1.1.4.10         else if (hm instanceof HMethod)
160 cananian 1.1.4.10             oldmut.removeDeclaredMethod((HMethod)oldm);
161 cananian 1.1.4.10         // update the memberMap
162 cananian 1.1.4.10         memberMap.remove(oldm);
163 cananian 1.1.4.10         memberMap.put(newm, hm);
164 cananian 1.1.4.10         // done!
165 cananian 1.3.2.1          assert hm.getDeclaringClass()==newDestination;
166 cananian 1.1.4.10     }
167 cananian 1.1.4.10 
168 cananian 1.1.4.4      // stub to help in reloading proxies.
169 cananian 1.1.4.4      HClassProxy load(String descriptor, HClass proxy) {
170 cananian 1.1.4.4          if (descCache.containsKey(descriptor))
171 cananian 1.1.4.4              ((HClassProxy) descCache.get(descriptor)).relink(proxy);
172 cananian 1.1.4.4          else
173 cananian 1.1.4.4              descCache.put(descriptor, new HClassProxy(this, proxy));
174 cananian 1.1.4.4          return (HClassProxy) descCache.get(descriptor);
175 cananian 1.1.4.4      }
176 cananian 1.1.4.1  
177 cananian 1.1.4.3      // Serializable
178 cananian 1.1.4.3      private void readObject(java.io.ObjectInputStream in)
179 cananian 1.1.4.3          throws java.io.IOException, ClassNotFoundException {
180 cananian 1.1.4.3          in.defaultReadObject();
181 cananian 1.1.4.8          // create new memberMap
182 cananian 1.1.4.3          // note that linker descCache is cleared magically on read, too.
183 cananian 1.1.4.3          memberMap = new HashMap();
184 cananian 1.1.4.8          // now restore the "odd" entries in the descCache.
185 cananian 1.1.4.8          for (Iterator it=((List)in.readObject()).iterator(); it.hasNext(); ) {
186 cananian 1.1.4.8              String descK = (String) it.next();
187 cananian 1.1.4.8              String descV = (String) it.next();
188 cananian 1.1.4.8              relink(forDescriptor(descK),forDescriptor(descV));
189 cananian 1.1.4.8          }
190 cananian 1.1.4.8          // done.
191 cananian 1.1.4.8      }
192 cananian 1.1.4.8      private void writeObject(java.io.ObjectOutputStream out)
193 cananian 1.1.4.8          throws java.io.IOException {
194 cananian 1.1.4.8          out.defaultWriteObject();
195 cananian 1.1.4.8          // write out list of relinked descriptors.
196 cananian 1.1.4.8          List l = new ArrayList();
197 cananian 1.6              for (Object descKO : descCache.keySet()) {
198 cananian 1.6                  String descK = (String) descKO;
199 cananian 1.1.4.8              String descV = forDescriptor(descK).getDescriptor();
200 cananian 1.1.4.8              if (descK.equals(descV)) continue; // not an interesting entry.
201 cananian 1.1.4.8              l.add(descK); l.add(descV);
202 cananian 1.1.4.8          }
203 cananian 1.1.4.8          out.writeObject(l);
204 cananian 1.1.4.3      }
205 cananian 1.1.4.3  
206 cananian 1.1.4.1      // WRAP/UNWRAP CODE
207 cananian 1.1.4.3      transient Map memberMap = new HashMap();
208 cananian 1.1.4.1  
209 cananian 1.1.4.1      HClass wrap(HClass hc) {
210 cananian 1.1.4.1          if (hc==null || hc.isPrimitive()) return hc;
211 cananian 1.1.4.1          return forDescriptor(hc.getDescriptor());
212 cananian 1.1.4.1      }
213 cananian 1.1.4.1      HClass unwrap(HClass hc) {
214 cananian 1.1.4.1          if (hc==null || hc.isPrimitive()) return hc;
215 bdemsky  1.1.4.12         /* If we "promote" the original class to an HClassSyn (to modify it)
216 bdemsky  1.1.4.12          * we must still 'unwrap' it to the unmodified version from the
217 bdemsky  1.1.4.12          * original linker, or reference-equality tests against it will fail.
218 bdemsky  1.1.4.12          * Example: searching for method foo(Object o) of class A after we've
219 bdemsky  1.1.4.12          * added a new field to Object.  Class A's method descriptor will
220 bdemsky  1.1.4.12          * still reference the "old" Object class. */
221 cananian 1.1.4.6          if (((HClassProxy)hc).sameLinker)
222 cananian 1.1.4.4              return linker.forDescriptor(hc.getDescriptor());
223 cananian 1.1.4.1          return ((HClassProxy)hc).proxy;
224 cananian 1.1.4.1      }
225 cananian 1.1.4.1      HField wrap(HField hf) {
226 cananian 1.1.4.1          if (hf==null) return null;
227 cananian 1.3.2.1          assert !(hf instanceof HFieldProxy &&
228 cananian 1.3.2.1                        ((HFieldProxy)hf).relinker==this) : "should never try to proxy a proxy of this same relinker";
229 cananian 1.1.4.1          HField result = (HField) memberMap.get(hf);
230 cananian 1.1.4.1          if (result==null) {
231 cananian 1.1.4.6              result = new HFieldProxy(this, hf);
232 cananian 1.3.2.1              assert result.getDeclaringClass().getLinker()==this;
233 cananian 1.1.4.1              memberMap.put(hf, result);
234 cananian 1.1.4.1          }
235 cananian 1.1.4.1          return result;
236 cananian 1.1.4.1      }
237 cananian 1.1.4.1      HMethod wrap(HMethod hm) {
238 cananian 1.1.4.1          if (hm==null) return null;
239 cananian 1.3.2.1          assert !(hm instanceof HMethodProxy &&
240 cananian 1.3.2.1                        ((HMethodProxy)hm).relinker==this) : "should never try to proxy a proxy of this same relinker";
241 cananian 1.1.4.1          if (hm instanceof HInitializer) return wrap((HInitializer)hm);
242 cananian 1.1.4.1          if (hm instanceof HConstructor) return wrap((HConstructor)hm);
243 cananian 1.1.4.1          HMethod result = (HMethodProxy) memberMap.get(hm);
244 cananian 1.1.4.1          if (result==null) {
245 cananian 1.1.4.6              result = new HMethodProxy(this, hm);
246 cananian 1.3.2.1              assert result.getDeclaringClass().getLinker()==this;
247 cananian 1.1.4.1              memberMap.put(hm, result);
248 cananian 1.1.4.1          }
249 cananian 1.1.4.1          return result;
250 cananian 1.1.4.1      }
251 cananian 1.1.4.1      HConstructor wrap(HConstructor hc) {
252 cananian 1.1.4.1          if (hc==null) return null;
253 cananian 1.1.4.1          HConstructor result = (HConstructorProxy) memberMap.get(hc);
254 cananian 1.1.4.1          if (result==null) {
255 cananian 1.3.2.1              assert !hc.getDeclaringClass().isArray();
256 cananian 1.1.4.1              result = new HConstructorProxy(this, hc);
257 cananian 1.1.4.1              memberMap.put(hc, result);
258 cananian 1.1.4.1          }
259 cananian 1.1.4.1          return result;
260 cananian 1.1.4.1      }
261 cananian 1.1.4.1      HInitializer wrap(HInitializer hi) {
262 cananian 1.1.4.1          if (hi==null) return null;
263 cananian 1.1.4.1          HInitializer result = (HInitializerProxy) memberMap.get(hi);
264 cananian 1.1.4.1          if (result==null) {
265 cananian 1.3.2.1              assert !hi.getDeclaringClass().isArray();
266 cananian 1.1.4.1              result = new HInitializerProxy(this, hi);
267 cananian 1.1.4.1              memberMap.put(hi, result);
268 cananian 1.1.4.1          }
269 cananian 1.1.4.1          return result;
270 cananian 1.1.4.1      }    
271 cananian 1.1.4.1      // array wrap/unwrap
272 cananian 1.1.4.1      HClass[] wrap(HClass hc[]) {
273 cananian 1.1.4.1          HClass[] result = new HClass[hc.length];
274 cananian 1.1.4.1          for (int i=0; i<result.length; i++)
275 cananian 1.1.4.1              result[i] = wrap(hc[i]);
276 cananian 1.1.4.1          return result;
277 cananian 1.1.4.1      }
278 cananian 1.1.4.1      HClass[] unwrap(HClass[] hc) {
279 cananian 1.1.4.1          HClass[] result = new HClass[hc.length];
280 cananian 1.1.4.1          for (int i=0; i<result.length; i++)
281 cananian 1.1.4.1              result[i] = unwrap(hc[i]);
282 cananian 1.1.4.1          return result;
283 cananian 1.1.4.1      }
284 cananian 1.1.4.1      HField[] wrap(HField hf[]) {
285 cananian 1.1.4.1          HField[] result = new HField[hf.length];
286 cananian 1.1.4.1          for (int i=0; i<result.length; i++)
287 cananian 1.1.4.1              result[i] = wrap(hf[i]);
288 cananian 1.1.4.1          return result;
289 cananian 1.1.4.1      }
290 cananian 1.1.4.1      HMethod[] wrap(HMethod hm[]) {
291 cananian 1.1.4.1          HMethod[] result = new HMethod[hm.length];
292 cananian 1.1.4.1          for (int i=0; i<result.length; i++)
293 cananian 1.1.4.1              result[i] = wrap(hm[i]);
294 cananian 1.1.4.1          return result;
295 cananian 1.1.4.1      }
296 cananian 1.1.4.1      HConstructor[] wrap(HConstructor hc[]) {
297 cananian 1.1.4.1          HConstructor[] result = new HConstructor[hc.length];
298 cananian 1.1.4.1          for (int i=0; i<result.length; i++)
299 cananian 1.1.4.1              result[i] = wrap(hc[i]);
300 cananian 1.1.4.1          return result;
301 cananian 1.1.4.1      }
302 cananian 1.2      }