1 cananian 1.1  // AbstractClassFixupRelinker.java, created Fri Jul  5 20:25:45 2002 by cananian
  2 cananian 1.1  // Copyright (C) 2000 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1  // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1  package harpoon.Analysis;
  5 cananian 1.1  
  6 cananian 1.1  import harpoon.ClassFile.HClass;
  7 cananian 1.4  import harpoon.ClassFile.HInitializer;
  8 cananian 1.5  import harpoon.ClassFile.HField;
  9 cananian 1.1  import harpoon.ClassFile.HMethod;
 10 cananian 1.1  import harpoon.ClassFile.Linker;
 11 cananian 1.1  import harpoon.ClassFile.Relinker;
 12 cananian 1.1  import harpoon.Util.ArrayIterator;
 13 cananian 1.10 import net.cscott.jutil.UniqueVector;
 14 cananian 1.10 import net.cscott.jutil.WorkSet;
 15 cananian 1.1  
 16 cananian 1.1  import java.lang.reflect.Modifier;
 17 cananian 1.9  import java.util.HashSet;
 18 cananian 1.9  import java.util.Iterator;
 19 cananian 1.9  import java.util.Set;
 20 cananian 1.1  /**
 21 cananian 1.1   * <code>AbstractClassFixupRelinker</code> is an extension of <code>Relinker</code>
 22 cananian 1.1   * which fixes up abstract classes so that they implement all the methods
 23 cananian 1.1   * of their interfaces (even if this implementation is via an abstract
 24 cananian 1.1   * method declaration).  The newer JDK1.4 compiler that Sun provides does
 25 cananian 1.1   * not put these method declarations in by default, unlike earlier
 26 cananian 1.1   * compilers, and this violates several assumptions made in FLEX.
 27 cananian 1.1   * The <code>AbstractClassFixupRelinker</code> remedies the situation.
 28 cananian 1.1   * 
 29 cananian 1.1   * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 30 cananian 1.11  * @version $Id: AbstractClassFixupRelinker.java,v 1.11 2004/02/08 03:19:12 cananian Exp $
 31 cananian 1.1   */
 32 cananian 1.1  public class AbstractClassFixupRelinker extends Relinker {
 33 cananian 1.1      
 34 cananian 1.1      /** Creates a <code>AbstractClassFixupRelinker</code>. */
 35 cananian 1.1      public AbstractClassFixupRelinker(Linker linker) {
 36 cananian 1.1          super(linker);
 37 cananian 1.1      }
 38 wbeebee  1.7  
 39 cananian 1.1      public HClass forDescriptor(String descriptor) {
 40 wbeebee  1.7          // We want to make sure that Linker.forDescriptor completes
 41 wbeebee  1.7          // entirely (entering the new class in the descriptor cache)
 42 wbeebee  1.7          // before we invoke it again.  HOWEVER, Linker.forDescriptor
 43 wbeebee  1.7          // will call *us* recursively e.g. to resolve arrays.  So it's
 44 wbeebee  1.7          // not safe to do our resolve() stuff (which will indirectly
 45 wbeebee  1.7          // cause further calls to Linker.forDescriptor) until we're
 46 wbeebee  1.7          // sure the top-level call to Linker.forDescriptor has finished.
 47 wbeebee  1.7          // The 'depth' variable here basically just keeps track of
 48 wbeebee  1.7          // whether this is a recursive invocation via Linker.forDescriptor
 49 wbeebee  1.7          // or a 'top-level' one; we only resolve after top-level calls.
 50 wbeebee  1.7          depth++;
 51 cananian 1.1          HClass hc = super.forDescriptor(descriptor);
 52 wbeebee  1.7          depth--;
 53 wbeebee  1.7          deferred.add(hc);
 54 wbeebee  1.7          if (depth==0) // this is a top-level call, it's safe to resolve.
 55 wbeebee  1.7              while (!deferred.isEmpty())
 56 wbeebee  1.7                  resolve(deferred.removeFirst());
 57 wbeebee  1.7          return hc;
 58 wbeebee  1.7      }
 59 wbeebee  1.7      /** Set of classes waiting to be fixed up. */
 60 wbeebee  1.7      WorkSet<HClass> deferred = new WorkSet<HClass>();
 61 wbeebee  1.7      /** Current level of recursion. */
 62 wbeebee  1.7      int depth=0;
 63 wbeebee  1.7  
 64 wbeebee  1.7      public HClass resolve(HClass hc) {
 65 cananian 1.1          // okay, now scan the class and fix it up.
 66 cananian 1.1          if (!hc.isInterface() && !done.contains(hc)) {
 67 cananian 1.2              done.add(hc);// properly handle incidental recursion inside fixup()
 68 cananian 1.2              // we should first fix up the superclass(es), if any.
 69 cananian 1.1              HClass sc = hc.getSuperclass();
 70 wbeebee  1.7              if (sc!=null) resolve(sc); // recurse!
 71 cananian 1.1              // okay, now fix this class up.
 72 cananian 1.1              fixup(hc);
 73 cananian 1.6  
 74 cananian 1.6              // and to prevent fixup from happening at inopportune times
 75 cananian 1.6              // (such as after we start modifying method signatures)
 76 cananian 1.6              // we're going to be aggressive in fixing up other classes
 77 cananian 1.6              // mentioned in fields and methods of this one.
 78 cananian 1.6  
 79 cananian 1.6              // fix up classes mentioned in method signatures
 80 cananian 1.11             for (HMethod hm : hc.getDeclaredMethods()) {
 81 wbeebee  1.7                  resolve(hm.getReturnType());
 82 cananian 1.5                  for (Iterator<HClass> it2=new ArrayIterator<HClass>
 83 cananian 1.5                           (hm.getParameterTypes()); it2.hasNext(); )
 84 wbeebee  1.7                      resolve(it2.next());
 85 cananian 1.5              }
 86 cananian 1.5              // and those in field signatures.
 87 cananian 1.5              for (Iterator<HField> it=new ArrayIterator<HField>
 88 cananian 1.5                       (hc.getDeclaredFields()); it.hasNext(); )
 89 wbeebee  1.7                  resolve(it.next().getType());
 90 cananian 1.1          }
 91 cananian 1.1          // done!
 92 cananian 1.1          return hc;
 93 cananian 1.1      }
 94 cananian 1.3      private static Set<HClass> done = new HashSet<HClass>();
 95 cananian 1.1      
 96 cananian 1.1      private void fixup(HClass hc) {
 97 cananian 1.1          for (Iterator<HClass> it=collectInterfaces(hc).iterator();
 98 cananian 1.1               it.hasNext(); )
 99 cananian 1.1              fixupOne(hc, it.next());
100 cananian 1.1      }
101 cananian 1.1      private void fixupOne(HClass hc, HClass anInterface) {
102 cananian 1.1          assert !hc.isInterface();
103 cananian 1.1          assert anInterface.isInterface();
104 cananian 1.1          // get list of interface methods.
105 cananian 1.11         for (HMethod hm : anInterface.getDeclaredMethods()) {
106 cananian 1.4              // could be a static initializer of the interface.
107 cananian 1.4              if (hm instanceof HInitializer) continue;
108 cananian 1.4              // otherwise this should be an interface method.
109 cananian 1.1              assert hm.isInterfaceMethod();
110 cananian 1.1              // okay, look this up as a method of hc.  if it doesn't exist,
111 cananian 1.1              // create it as a public abstract method.
112 cananian 1.1              HMethod hmm = hc.getMethod(hm.getName(), hm.getParameterTypes());
113 cananian 1.1              if (hmm.isInterfaceMethod()) {
114 cananian 1.1                  // NOT IMPLEMENTED IN CLASS!  this better be an abstract class:
115 cananian 1.1                  assert Modifier.isAbstract(hc.getModifiers()) :
116 cananian 1.1                      "interface method "+hm+" not implemented in non-abstract "+
117 cananian 1.1                      "class "+hc;
118 cananian 1.1                  // okay, it's an abstract class, so make an abstract
119 cananian 1.1                  // implementation method.
120 cananian 1.1                  HMethod nm=hc.getMutator().addDeclaredMethod
121 cananian 1.1                      (hm.getName(), hm.getDescriptor());
122 cananian 1.1                  nm.getMutator().setModifiers
123 cananian 1.1                      (Modifier.PUBLIC | Modifier.ABSTRACT);
124 cananian 1.1                  nm.getMutator().setExceptionTypes(hm.getExceptionTypes());
125 cananian 1.1              }
126 cananian 1.1          }
127 cananian 1.1      }
128 cananian 1.1  
129 cananian 1.1      private static Set<HClass> collectInterfaces(HClass hc) {
130 cananian 1.1          UniqueVector<HClass> uv = new UniqueVector<HClass>();
131 cananian 1.1          for (Iterator<HClass> it=new ArrayIterator<HClass>(hc.getInterfaces());
132 cananian 1.1               it.hasNext(); )
133 cananian 1.1              uv.add(it.next());
134 cananian 1.1          // okay, uv now has the basic interfaces.  we just have to
135 cananian 1.1          // go through it from beginning to end to add all superinterfaces.
136 cananian 1.1          for (int i=0; i<uv.size(); i++)
137 cananian 1.1              for (Iterator<HClass> it=new ArrayIterator<HClass>
138 cananian 1.1                       (uv.get(i).getInterfaces()); it.hasNext(); )
139 cananian 1.1                  uv.add(it.next());
140 cananian 1.1          // done!
141 cananian 1.1          return uv;
142 cananian 1.1      }
143 cananian 1.1  }