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 }