1 cananian 1.1.4.1 // InterfaceMethodMap.java, created Tue Jan 19 17:10:17 1999 by pnkfelix
  2 cananian 1.1.4.5 // Copyright (C) 1998 Felix S. Klock II <pnkfelix@mit.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.Backend.Analysis;
  5 cananian 1.1.4.1 
  6 cananian 1.1.4.1 import java.lang.reflect.Modifier;
  7 cananian 1.1.4.1 import java.util.Collections;
  8 cananian 1.1.4.1 import java.util.Enumeration;
  9 cananian 1.1.4.1 import java.util.Hashtable;
 10 cananian 1.1.4.1 import java.util.Vector;
 11 cananian 1.1.4.1 
 12 cananian 1.1.4.1 import harpoon.ClassFile.HClass;
 13 cananian 1.1.4.1 import harpoon.ClassFile.HConstructor;
 14 cananian 1.1.4.1 import harpoon.ClassFile.HMethod;
 15 cananian 1.1.4.1 import harpoon.Backend.Maps.MethodMap;
 16 cananian 1.1.4.1 
 17 cananian 1.1.4.1 import harpoon.Util.Util;
 18 cananian 1.5     import net.cscott.jutil.UniqueVector;
 19 cananian 1.1.4.1 
 20 cananian 1.1.4.1 import harpoon.Analysis.ClassHierarchy;
 21 cananian 1.1.4.1 
 22 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.Color;
 23 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.ColorableNode;
 24 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.ColorFactory;
 25 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.SimpleGraphColorer;
 26 pnkfelix 1.1.4.4 import harpoon.Analysis.GraphColoring.UnboundedGraphColorer;
 27 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.SparseGraph;
 28 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.SparseNode;
 29 cananian 1.1.4.1 import harpoon.Analysis.GraphColoring.IllegalEdgeException;
 30 cananian 1.1.4.1 /**
 31 cananian 1.1.4.1  * <code>InterfaceMethodMap</code> provides a mapping from interface
 32 cananian 1.1.4.1  * methods to the offset that the method-pointers should have on the
 33 cananian 1.1.4.1  * object layout.
 34 cananian 1.1.4.1  * 
 35 cananian 1.1.4.5  * @author  Felix S. Klock II <pnkfelix@mit.edu>
 36 cananian 1.5      * @version $Id: InterfaceMethodMap.java,v 1.5 2004/02/08 01:57:24 cananian Exp $
 37 cananian 1.1.4.1  */
 38 cananian 1.1.4.1 
 39 cananian 1.1.4.1 public class InterfaceMethodMap extends MethodMap {
 40 cananian 1.1.4.1 
 41 cananian 1.1.4.1     private static boolean DEBUG = false;
 42 cananian 1.1.4.1 
 43 cananian 1.1.4.1     // maps HMethod to HmNode
 44 cananian 1.1.4.1     private Hashtable mtable;
 45 cananian 1.1.4.1     private HmNodeFactory factory;
 46 cananian 1.1.4.1             
 47 cananian 1.1.4.1     /** Creates a <code>InterfaceMethodMap</code> for interfaces in
 48 cananian 1.1.4.1         <code>hclasses</code>. 
 49 cananian 1.1.4.1         <BR> <B>requires:</B> <code>hclasses</code> is an
 50 cananian 1.1.4.1                               <code>Enumeration</code> of
 51 cananian 1.1.4.1                               <code>HClass</code> objects.
 52 cananian 1.1.4.1         <BR> <B>modifies:</B> <code>hclasses</code>
 53 cananian 1.1.4.1         <BR> <B>effects:</B> Iterates through <code>hclasses</code>,
 54 cananian 1.1.4.1                       accumulating all of the interface-methods and
 55 cananian 1.1.4.1                       returns a method->int mapping, where the integer
 56 cananian 1.1.4.1                       returned represents the placement of the method.
 57 cananian 1.1.4.1                       This method is not guaranteed to use any
 58 cananian 1.1.4.1                       information besides what is passed to it;
 59 cananian 1.1.4.1                       methods from interfaces not in
 60 cananian 1.1.4.1                       <code>hclasses</code> are not required to be
 61 cananian 1.1.4.1                       included in the method map returned, and
 62 cananian 1.1.4.1                       interferences from classes not in
 63 cananian 1.1.4.1                       <code>hclasses</code> are not required to be
 64 cananian 1.1.4.1                       accounted for.  
 65 cananian 1.1.4.1         @see HClass
 66 cananian 1.1.4.1         @see harpoon.Analysis.ClassHierarchy
 67 cananian 1.1.4.1         @see harpoon.Main.CallGraph
 68 cananian 1.1.4.1      */
 69 cananian 1.1.4.1     public InterfaceMethodMap( Enumeration hclasses ) {
 70 cananian 1.1.4.1         mtable = new Hashtable();
 71 cananian 1.1.4.1         factory = new HmNodeFactory();
 72 cananian 1.1.4.1 
 73 cananian 1.1.4.1         SparseGraph g = assembleGraph( hclasses );
 74 pnkfelix 1.1.4.4         UnboundedGraphColorer colorer =
 75 pnkfelix 1.1.4.4             new UnboundedGraphColorer(new SimpleGraphColorer(),
 76 pnkfelix 1.1.4.4                                       new SlotColorFactory() );
 77 cananian 1.1.4.1         colorer.findColoring( g );
 78 cananian 1.1.4.1         
 79 cananian 1.1.4.1     }
 80 cananian 1.1.4.1     /** Creates a <code>InterfaceMethodMap</code> for interfaces in
 81 cananian 1.1.4.1         the given <code>ClassHierarchy</code> <code>ch</code>. 
 82 cananian 1.1.4.1         <BR> <B>effects:</B> Iterates through the class hierarchy
 83 pnkfelix 1.1.4.4              accumulating all of the interface-methods and returns a
 84 pnkfelix 1.1.4.4              method->int mapping, where the integer returned
 85 pnkfelix 1.1.4.4              represents the placement of the method.  This method is
 86 pnkfelix 1.1.4.4              not guaranteed to use any information besides what is
 87 pnkfelix 1.1.4.4              passed to it; methods from interfaces not in the class
 88 pnkfelix 1.1.4.4              hierarchy are not required to be included in the method
 89 pnkfelix 1.1.4.4              map returned, and interferences from classes not in 
 90 pnkfelix 1.1.4.4              the class hierarchy are not required to be accounted for.
 91 cananian 1.1.4.1         @see HClass
 92 cananian 1.1.4.1         @see harpoon.Analysis.ClassHierarchy
 93 cananian 1.1.4.1         @see harpoon.Main.CallGraph
 94 cananian 1.1.4.1      */
 95 cananian 1.1.4.1     public InterfaceMethodMap( ClassHierarchy ch ) {
 96 cananian 1.1.4.1         this(Collections.enumeration(ch.classes()));
 97 cananian 1.1.4.1     }
 98 cananian 1.1.4.1     
 99 cananian 1.1.4.1     /** Returns an ordering of the given method. 
100 cananian 1.1.4.1         <BR> <B>requires:</B> <code>this</code> contains an ordering
101 cananian 1.1.4.1                               for <code>hm</code> 
102 cananian 1.1.4.1         <BR> <B>effects:</B> returns the zero-indexed integer
103 cananian 1.1.4.1                              corresponding to <code>hm</code>'s
104 cananian 1.1.4.1                              placement in the ordering.  
105 cananian 1.1.4.1      */
106 cananian 1.1.4.1     public int methodOrder( HMethod hm ) {
107 cananian 1.1.4.1         // method must be an interface method and thus public,
108 cananian 1.1.4.1         // not static, and not a constructor.
109 cananian 1.3.2.1         assert hm.isInterfaceMethod() && !hm.isStatic();
110 cananian 1.3.2.1         assert Modifier.isPublic(hm.getModifiers());
111 cananian 1.3.2.1         assert !(hm instanceof HConstructor);
112 cananian 1.1.4.7         // also disallow methods inherited from java.lang.Object
113 cananian 1.3.2.1         assert includeMethod(hm) : hm;
114 cananian 1.1.4.1         HmNode node = (HmNode) mtable.get( hm );
115 cananian 1.3.2.1         assert node != null : "InterfaceMethodMap must contain "+
116 cananian 1.3.2.1                     "a mapping for " + hm;
117 cananian 1.1.4.1         SlotColor c = (SlotColor) node.getColor();
118 cananian 1.1.4.1         return c.index;
119 cananian 1.1.4.1     }
120 cananian 1.1.4.1 
121 cananian 1.1.4.1     
122 cananian 1.1.4.1     // -- HELPER METHODS FOR CONSTUCTOR FOLLOW --
123 cananian 1.1.4.1 
124 cananian 1.1.4.1     /** Assembles a graph for use by the graph colorer.
125 cananian 1.1.4.1         <BR> <B>requires:</B> <code>hclasses</code> is an enumeration of 
126 cananian 1.1.4.1                               <code>HClass</code> objects
127 cananian 1.1.4.1         <BR> <B>modifies:</B> <code>hclasses</code>
128 cananian 1.1.4.1         <BR> <B>effects:</B> Iterates through <code>hclasses</code>,
129 cananian 1.1.4.1                              building up a graph with nodes
130 cananian 1.1.4.1                              corresponding to the methods of the
131 cananian 1.1.4.1                              interfaces in <code>hclasses</code> and
132 cananian 1.1.4.1                              the edges corresponding to interferences
133 cananian 1.1.4.1                              between the methods (due to being
134 cananian 1.1.4.1                              implemented by the same classes). 
135 cananian 1.1.4.1     */
136 cananian 1.1.4.1     private SparseGraph assembleGraph( Enumeration hclasses ) {
137 cananian 1.1.4.1         UniqueVector classes = new UniqueVector();
138 cananian 1.1.4.1         UniqueVector interfaces = new UniqueVector();
139 cananian 1.1.4.1         SparseGraph graph = new SparseGraph();
140 cananian 1.1.4.1         
141 cananian 1.1.4.1         // MAKE NODES (methods of interfaces)
142 cananian 1.1.4.1         while ( hclasses.hasMoreElements() ) {
143 cananian 1.1.4.1             HClass hc = (HClass) hclasses.nextElement();
144 cananian 1.1.4.1             if (hc.isInterface()) { // INTERFACE
145 cananian 1.1.4.1                 // make nodes for each method and add them to graph
146 cananian 1.1.4.1                 if (DEBUG) System.out.println("Adding methods of interface " + 
147 cananian 1.1.4.1                                               hc + " to graph");
148 cananian 1.1.4.1                 
149 cananian 1.1.4.1                 interfaces.addElement( hc );
150 cananian 1.1.4.1                 HMethod[] methods = hc.getMethods();
151 cananian 1.1.4.1                 for (int i=0; i<methods.length; i++) {
152 cananian 1.1.4.1                     if (includeMethod( methods[i] )) {
153 cananian 1.1.4.1                         HmNode mnode = factory.getNode( methods[i] );
154 cananian 1.1.4.1                         if (DEBUG) System.out.println
155 cananian 1.1.4.1                                        ("Adding method/node " + 
156 cananian 1.1.4.1                                         methods[i] + " / " + mnode);
157 cananian 1.1.4.1                         graph.addNode( mnode );
158 cananian 1.1.4.1                     }
159 cananian 1.1.4.1                 }
160 cananian 1.1.4.1             } else {                // CLASS
161 cananian 1.1.4.1                 // add hclasses to classes for later edge searching
162 cananian 1.1.4.1                 if (DEBUG) System.out.println
163 cananian 1.1.4.1                                ("Storing " + hc + " for later searching");
164 cananian 1.1.4.1                 classes.addElement( hc );
165 cananian 1.1.4.1             }
166 cananian 1.1.4.1         }
167 cananian 1.1.4.1         
168 cananian 1.1.4.1         // MAKE EDGES (between nodes belonging to interfaces
169 cananian 1.1.4.1         // implemented by same class) 
170 cananian 1.1.4.1         for (int i=0; i<classes.size(); i++) {
171 cananian 1.1.4.1             HClass hc = (HClass) classes.elementAt(i);
172 cananian 1.1.4.1             UniqueVector cNodes = new UniqueVector();
173 cananian 1.1.4.1             
174 cananian 1.1.4.1             // backtrack through hierarchy, adding all methods of all
175 cananian 1.1.4.1             // interfaces found.
176 cananian 1.1.4.1             while( hc != null ) {
177 cananian 1.1.4.1                 HClass[] ifaces = hc.getInterfaces();
178 cananian 1.1.4.1                 for (int j=0; j<ifaces.length; j++) {
179 cananian 1.1.4.1                     Vector nodes = findNodesFor( ifaces[j] );
180 cananian 1.1.4.1                     for(int k=0; k<nodes.size(); k++) {
181 cananian 1.1.4.1                         cNodes.addElement( nodes.elementAt( k ));
182 cananian 1.1.4.1                     }
183 cananian 1.1.4.1                 }
184 cananian 1.1.4.1                 hc = hc.getSuperclass();
185 cananian 1.1.4.1             }
186 cananian 1.1.4.1             
187 cananian 1.1.4.1             for (int j=0; j<cNodes.size(); j++) {
188 cananian 1.1.4.1                 HmNode nodeA = (HmNode) cNodes.elementAt(j);
189 cananian 1.1.4.1                 for (int k=j+1; k<cNodes.size(); k++) {
190 cananian 1.1.4.1                     HmNode nodeB = (HmNode) cNodes.elementAt(k);
191 cananian 1.1.4.1                     try {
192 cananian 1.1.4.1                         if (DEBUG) System.out.println
193 cananian 1.1.4.1                                        ("Making an edge between " + nodeA + 
194 cananian 1.1.4.1                                         " and " + nodeB + " in graph.");
195 cananian 1.1.4.1                         graph.makeEdge( nodeA, nodeB );
196 cananian 1.1.4.1                     } catch (IllegalEdgeException e) {
197 cananian 1.1.4.1                         // ignore (algorithm is dumb and doesn't know
198 cananian 1.1.4.1                         // better. 
199 cananian 1.1.4.1                     }
200 cananian 1.1.4.1                 }
201 cananian 1.1.4.1             }
202 cananian 1.1.4.1         }
203 cananian 1.1.4.1         
204 cananian 1.1.4.1         return graph;
205 cananian 1.1.4.1     }
206 cananian 1.1.4.1 
207 cananian 1.1.4.1         
208 cananian 1.1.4.1     /** Checks suitability of an HMethod for inclusion in a graph.
209 cananian 1.1.4.1         <BR> <B>effects:</B> if <code>m</code> is a method of
210 cananian 1.1.4.1                              <code>java.lang.Object</code> then
211 cananian 1.1.4.1                              returns false.  Else returns true. 
212 cananian 1.1.4.1     */
213 cananian 1.1.4.1     private static boolean includeMethod(HMethod m) {
214 cananian 1.1.4.7         // can't just check m.getDeclaringClass() because some interfaces
215 cananian 1.1.4.7         // re-define methods from java.lang.Object.  Try to find the
216 cananian 1.1.4.7         // method's signature in java.lang.Object instead.
217 cananian 1.1.4.7         try {
218 cananian 1.1.4.7             // safe to getLinker because m.getDeclaringClass() is non-primitive
219 cananian 1.1.4.7             m.getDeclaringClass().getLinker().forName("java.lang.Object")
220 cananian 1.1.4.7                 .getMethod(m.getName(), m.getDescriptor());
221 cananian 1.1.4.7             // if this call succeeded, then *don't* include this method as
222 cananian 1.1.4.7             // an interface method!
223 cananian 1.1.4.7             return false;
224 cananian 1.1.4.7         } catch (NoSuchMethodError nsme) {
225 cananian 1.1.4.7             return true;
226 cananian 1.1.4.7         }
227 cananian 1.1.4.1     }
228 cananian 1.1.4.1 
229 cananian 1.1.4.1     /** Generates a <code>Vector</code> of <code>HmNode</code>s for
230 cananian 1.1.4.1         methods in an interface.  
231 cananian 1.1.4.1         <BR> <B>requires:</B> <code>interfce</code> is an HClass
232 cananian 1.1.4.1                               representing an interface, and there are
233 cananian 1.1.4.1                               no circular interface extensions 
234 cananian 1.1.4.1                               (ie "A extends B" and "B extends A")
235 cananian 1.1.4.1         <BR> <B>effects:</B> Iterates over the accessible methods of
236 cananian 1.1.4.1                              <code>interfce</code>, accumulating them
237 cananian 1.1.4.1                              in a <code>Vector</code> of
238 cananian 1.1.4.1                              <code>HmNode</code>s.  Methods of
239 cananian 1.1.4.1                              superinterfaces are included, but the
240 cananian 1.1.4.1                              methods of Object are not.  Returns the
241 cananian 1.1.4.1                              newly built <code>Vector</code> of
242 cananian 1.1.4.1                              <code>HmNode</code>s.
243 cananian 1.1.4.1     */
244 cananian 1.1.4.1     private Vector findNodesFor( HClass interfce ) {
245 cananian 1.3.2.1         assert interfce.isInterface();
246 cananian 1.1.4.1         if (DEBUG) System.out.println("Finding nodes for " + interfce);
247 cananian 1.1.4.1         Vector nodes = new Vector();
248 cananian 1.1.4.1         HMethod[] methods = interfce.getMethods();
249 cananian 1.1.4.1         for (int i=0; i<methods.length; i++) {
250 cananian 1.1.4.1             if (includeMethod( methods[i] )) {
251 cananian 1.1.4.1                 if (DEBUG) System.out.println
252 cananian 1.1.4.1                                ("Adding method " + methods[ i ]);
253 cananian 1.1.4.1                 nodes.addElement( factory.getNode( methods[ i ] ) ); 
254 cananian 1.1.4.1             }
255 cananian 1.1.4.1         }
256 cananian 1.1.4.1         
257 cananian 1.1.4.1         // this sequence of code is unnecessary; HClass.getMethods()
258 cananian 1.1.4.1         // now performs according to spec.
259 cananian 1.1.4.1         /*
260 cananian 1.1.4.1           HClass[] superinterfaces = interfce.getInterfaces(); 
261 cananian 1.1.4.1           for (int i=0; i<superinterfaces.length; i++) { 
262 cananian 1.1.4.1             Vector v = findNodesFor( superinterfaces[i] ); 
263 cananian 1.1.4.1             for (int j=0; j<v.size(); j++) { 
264 cananian 1.1.4.1                 nodes.addElement(v.elementAt(j)); 
265 cananian 1.1.4.1                 } 
266 cananian 1.1.4.1             }
267 cananian 1.1.4.1         */
268 cananian 1.1.4.1         
269 cananian 1.1.4.1         return nodes;
270 cananian 1.1.4.1     }
271 cananian 1.1.4.1         
272 cananian 1.1.4.1     /** Produces HmNodes from HMethods, enforcing a mapping from a
273 cananian 1.1.4.1         method's name and set of argument types to one unique node,
274 cananian 1.1.4.1         regardless of which interface the method appears in.
275 cananian 1.1.4.6         <p>
276 cananian 1.1.4.6         Interface methods with the same name and descriptor always
277 cananian 1.1.4.6         map to the same slot, either due to single or multiple inheritance.
278 cananian 1.1.4.6         For example, A.foo() maps to the same slot as B.foo() if interface
279 cananian 1.1.4.6         B extends interface A; if interface B also extends interface C
280 cananian 1.1.4.6         and C has C.foo() then all three methods would always map to the
281 cananian 1.1.4.6         same slot (it is impossible to give C.foo() a different
282 cananian 1.1.4.6         implementation from A.foo() in any class which implements both A
283 cananian 1.1.4.6         and C).
284 cananian 1.1.4.1     */
285 cananian 1.1.4.1     private class HmNodeFactory {
286 cananian 1.1.4.1         // maps HmNode to HmNode
287 cananian 1.1.4.1         Hashtable ntable;
288 cananian 1.1.4.1         
289 cananian 1.1.4.1         HmNodeFactory() {
290 cananian 1.1.4.1             ntable = new Hashtable();
291 cananian 1.1.4.1         }
292 cananian 1.1.4.1         
293 cananian 1.1.4.1         HmNode getNode( HMethod hm ) {
294 cananian 1.1.4.1             HmNode temp = new HmNode( hm );
295 cananian 1.1.4.1             HmNode real = (HmNode) ntable.get( temp );
296 cananian 1.1.4.1             HmNode rtrn;
297 cananian 1.1.4.1             if (real == null) {
298 cananian 1.1.4.1                 ntable.put( temp, temp );
299 cananian 1.1.4.1                 rtrn = temp;
300 cananian 1.1.4.1             } else {
301 cananian 1.1.4.1                 rtrn = real;
302 cananian 1.1.4.1             }
303 cananian 1.1.4.1             mtable.put( hm, rtrn );
304 cananian 1.1.4.1             return rtrn;
305 cananian 1.1.4.1         }
306 cananian 1.1.4.1     }
307 cananian 1.1.4.1     
308 cananian 1.1.4.1     
309 cananian 1.1.4.1     /** HmNode is an extension of a SparseNode suitable for
310 cananian 1.1.4.1         representing a method of an interface.
311 cananian 1.1.4.1     */
312 cananian 1.1.4.1     private class HmNode extends SparseNode {
313 cananian 1.1.4.6         final String name, desc;
314 cananian 1.1.4.1         int hash;
315 cananian 1.1.4.1         
316 cananian 1.1.4.1         HmNode( HMethod hm ) {
317 cananian 1.1.4.1             name = hm.getName();
318 cananian 1.1.4.6             desc = hm.getDescriptor();
319 cananian 1.1.4.6             hash = name.hashCode() ^ desc.hashCode();
320 cananian 1.1.4.1         }
321 cananian 1.1.4.1         
322 cananian 1.1.4.1         public int hashCode() { return hash; }
323 cananian 1.1.4.1 
324 cananian 1.1.4.1         public boolean equals(Object o) {
325 cananian 1.1.4.1             HmNode n;
326 cananian 1.1.4.1             if (o==null) return false;
327 cananian 1.1.4.1             if (this==o) return true;
328 cananian 1.1.4.1             try { n = (HmNode) o; }
329 cananian 1.1.4.1             catch (ClassCastException e) { return false; }
330 cananian 1.1.4.6             if (n.hash!=this.hash) return false;
331 cananian 1.1.4.6             return n.name.equals(this.name) && n.desc.equals(this.desc);
332 cananian 1.1.4.1         }
333 cananian 1.1.4.1 
334 cananian 1.1.4.1         public String toString() {
335 cananian 1.1.4.6             return "HMethod Node [ " + name + desc + " " + hash +" ]";
336 cananian 1.1.4.1         }
337 cananian 1.1.4.1     }
338 cananian 1.1.4.1 
339 cananian 1.1.4.1     /** Simple implementation of a ColorFactory for interface slots. */
340 cananian 1.1.4.1     private class SlotColorFactory extends ColorFactory {
341 cananian 1.1.4.1         int counter;
342 cananian 1.1.4.1 
343 cananian 1.1.4.1         SlotColorFactory() {
344 cananian 1.1.4.1             counter = 0;
345 cananian 1.1.4.1         }
346 cananian 1.1.4.1 
347 cananian 1.1.4.1         protected Color newColor() {
348 cananian 1.1.4.1             counter++;
349 cananian 1.1.4.1             return new SlotColor(counter);
350 cananian 1.1.4.1         }
351 cananian 1.1.4.1     }
352 cananian 1.1.4.1 
353 cananian 1.1.4.1     /** Simple implementation of a Color for interface slots. */
354 cananian 1.1.4.1     private class SlotColor extends Color {
355 cananian 1.1.4.1         int index;
356 cananian 1.1.4.1         SlotColor(int i) { index = i; }
357 pnkfelix 1.1.4.3         public String toString() { return "SlotColor"+index; }
358 cananian 1.1.4.1     }
359 cananian 1.1.4.1 }
360 cananian 1.2