KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > Hierarchy


1 /*
2  * Bytecode Analysis Framework
3  * Copyright (C) 2003,2004 University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.ba;
21
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
26
27 import org.apache.bcel.Constants;
28 import org.apache.bcel.Repository;
29 import org.apache.bcel.classfile.ExceptionTable;
30 import org.apache.bcel.classfile.Field;
31 import org.apache.bcel.classfile.JavaClass;
32 import org.apache.bcel.classfile.Method;
33 import org.apache.bcel.generic.ArrayType;
34 import org.apache.bcel.generic.ConstantPoolGen;
35 import org.apache.bcel.generic.FieldInstruction;
36 import org.apache.bcel.generic.INVOKESTATIC;
37 import org.apache.bcel.generic.Instruction;
38 import org.apache.bcel.generic.InvokeInstruction;
39 import org.apache.bcel.generic.ObjectType;
40 import org.apache.bcel.generic.ReferenceType;
41 import org.apache.bcel.generic.Type;
42
43 import edu.umd.cs.findbugs.SystemProperties;
44 import edu.umd.cs.findbugs.annotations.NonNull;
45 import edu.umd.cs.findbugs.ba.type.TypeFrame;
46
47 /**
48  * Facade for class hierarchy queries.
49  * These typically access the class hierarchy using
50  * the {@link org.apache.bcel.Repository} class. Callers should generally
51  * expect to handle ClassNotFoundException for when referenced
52  * classes can't be found.
53  *
54  * @author David Hovemeyer
55  */

56 public class Hierarchy {
57     private static final boolean DEBUG_METHOD_LOOKUP =
58         SystemProperties.getBoolean("hier.lookup.debug");
59
60     /**
61      * Type of java.lang.Exception.
62      */

63     public static final ObjectType EXCEPTION_TYPE = ObjectTypeFactory.getInstance("java.lang.Exception");
64     /**
65      * Type of java.lang.Error.
66      */

67     public static final ObjectType ERROR_TYPE = ObjectTypeFactory.getInstance("java.lang.Error");
68     /**
69      * Type of java.lang.RuntimeException.
70      */

71     public static final ObjectType RUNTIME_EXCEPTION_TYPE = ObjectTypeFactory.getInstance("java.lang.RuntimeException");
72
73     /**
74      * Determine whether one class (or reference type) is a subtype
75      * of another.
76      *
77      * @param clsName the name of the class or reference type
78      * @param possibleSupertypeClassName the name of the possible superclass
79      * @return true if clsName is a subtype of possibleSupertypeClassName,
80      * false if not
81      */

82     public static boolean isSubtype(String JavaDoc clsName, String JavaDoc possibleSupertypeClassName) throws ClassNotFoundException JavaDoc {
83         ObjectType cls = ObjectTypeFactory.getInstance(clsName);
84         ObjectType superCls = ObjectTypeFactory.getInstance(possibleSupertypeClassName);
85         return isSubtype(cls, superCls);
86     }
87
88     /**
89      * Determine if one reference type is a subtype of another.
90      *
91      * @param t a reference type
92      * @param possibleSupertype the possible supertype
93      * @return true if t is a subtype of possibleSupertype,
94      * false if not
95      */

96     public static boolean isSubtype(ReferenceType t, ReferenceType possibleSupertype) throws ClassNotFoundException JavaDoc {
97         Map JavaDoc<ReferenceType, Boolean JavaDoc> subtypes = subtypeCache.get(possibleSupertype);
98         if (subtypes == null) {
99             subtypes = new HashMap JavaDoc<ReferenceType, Boolean JavaDoc>();
100             subtypeCache.put(possibleSupertype, subtypes);
101         }
102         Boolean JavaDoc result = subtypes.get(t);
103         if (result == null) {
104             result = Boolean.valueOf(t.isAssignmentCompatibleWith(possibleSupertype));
105             subtypes.put(t, result);
106         }
107         return result;
108     }
109
110     static Map JavaDoc<ReferenceType, Map JavaDoc<ReferenceType, Boolean JavaDoc>> subtypeCache = new HashMap JavaDoc<ReferenceType, Map JavaDoc<ReferenceType, Boolean JavaDoc>> ();
111     /**
112      * Determine if the given ObjectType reference represents
113      * a <em>universal</em> exception handler. That is,
114      * one that will catch any kind of exception.
115      *
116      * @param catchType the ObjectType of the exception handler
117      * @return true if catchType is null, or if catchType is
118      * java.lang.Throwable
119      */

120     public static boolean isUniversalExceptionHandler(ObjectType catchType) {
121         return catchType == null || catchType.equals(Type.THROWABLE);
122     }
123
124     /**
125      * Determine if the given ObjectType refers to an unchecked
126      * exception (RuntimeException or Error).
127      */

128     public static boolean isUncheckedException(ObjectType type) throws ClassNotFoundException JavaDoc {
129         return isSubtype(type, RUNTIME_EXCEPTION_TYPE) || isSubtype(type, ERROR_TYPE);
130     }
131
132     /**
133      * Determine if method whose name and signature is specified
134      * is a monitor wait operation.
135      *
136      * @param methodName name of the method
137      * @param methodSig signature of the method
138      * @return true if the method is a monitor wait, false if not
139      */

140     public static boolean isMonitorWait(String JavaDoc methodName, String JavaDoc methodSig) {
141         return methodName.equals("wait") &&
142                 (methodSig.equals("()V") || methodSig.equals("(J)V") || methodSig.equals("(JI)V"));
143     }
144     
145     /**
146      * Determine if given Instruction is a monitor wait.
147      *
148      * @param ins the Instruction
149      * @param cpg the ConstantPoolGen for the Instruction
150      *
151      * @return true if the instruction is a monitor wait, false if not
152      */

153     public static boolean isMonitorWait(Instruction ins, ConstantPoolGen cpg) {
154         if (!(ins instanceof InvokeInstruction))
155             return false;
156         if (ins.getOpcode() == Constants.INVOKESTATIC)
157             return false;
158         
159         InvokeInstruction inv = (InvokeInstruction) ins;
160         String JavaDoc methodName = inv.getMethodName(cpg);
161         String JavaDoc methodSig = inv.getSignature(cpg);
162         
163         return isMonitorWait(methodName, methodSig);
164     }
165
166     /**
167      * Determine if method whose name and signature is specified
168      * is a monitor notify operation.
169      *
170      * @param methodName name of the method
171      * @param methodSig signature of the method
172      * @return true if the method is a monitor notify, false if not
173      */

174     public static boolean isMonitorNotify(String JavaDoc methodName, String JavaDoc methodSig) {
175         return (methodName.equals("notify") || methodName.equals("notifyAll")) &&
176                 methodSig.equals("()V");
177     }
178     /**
179      * Determine if given Instruction is a monitor wait.
180      *
181      * @param ins the Instruction
182      * @param cpg the ConstantPoolGen for the Instruction
183      *
184      * @return true if the instruction is a monitor wait, false if not
185      */

186     public static boolean isMonitorNotify(Instruction ins, ConstantPoolGen cpg) {
187         if (!(ins instanceof InvokeInstruction))
188             return false;
189         if (ins.getOpcode() == Constants.INVOKESTATIC)
190             return false;
191         
192         InvokeInstruction inv = (InvokeInstruction) ins;
193         String JavaDoc methodName = inv.getMethodName(cpg);
194         String JavaDoc methodSig = inv.getSignature(cpg);
195         
196         return isMonitorNotify(methodName, methodSig);
197     }
198
199     /**
200      * Look up the method referenced by given InvokeInstruction.
201      * This method does <em>not</em> look for implementations in
202      * super or subclasses according to the virtual dispatch rules.
203      *
204      * @param inv the InvokeInstruction
205      * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
206      * @return the JavaClassAndMethod, or null if no such method is defined in the class
207      */

208     public static JavaClassAndMethod findExactMethod(InvokeInstruction inv, ConstantPoolGen cpg) throws ClassNotFoundException JavaDoc {
209         return findExactMethod(inv, cpg, ANY_METHOD);
210     }
211
212     /**
213      * Look up the method referenced by given InvokeInstruction.
214      * This method does <em>not</em> look for implementations in
215      * super or subclasses according to the virtual dispatch rules.
216      *
217      * @param inv the InvokeInstruction
218      * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
219      * @param chooser JavaClassAndMethodChooser to use to pick the method from among the candidates
220      * @return the JavaClassAndMethod, or null if no such method is defined in the class
221      */

222     public static JavaClassAndMethod findExactMethod(
223             InvokeInstruction inv,
224             ConstantPoolGen cpg,
225             JavaClassAndMethodChooser chooser) throws ClassNotFoundException JavaDoc {
226         String JavaDoc className = inv.getClassName(cpg);
227         String JavaDoc methodName = inv.getName(cpg);
228         String JavaDoc methodSig = inv.getSignature(cpg);
229
230         JavaClass jclass = Repository.lookupClass(className);
231         return findMethod(jclass, methodName, methodSig, chooser);
232     }
233     
234     /**
235      * Visit all superclass methods which the given method overrides.
236      *
237      * @param method the method
238      * @param chooser chooser which visits each superclass method
239      * @return the chosen method, or null if no method is chosen
240      * @throws ClassNotFoundException
241      */

242     public static JavaClassAndMethod visitSuperClassMethods(
243             JavaClassAndMethod method, JavaClassAndMethodChooser chooser) throws ClassNotFoundException JavaDoc {
244         return findMethod(
245                 method.getJavaClass().getSuperClasses(),
246                 method.getMethod().getName(),
247                 method.getMethod().getSignature(),
248                 chooser);
249     }
250     
251     /**
252      * Visit all superinterface methods which the given method implements.
253      *
254      * @param method the method
255      * @param chooser chooser which visits each superinterface method
256      * @return the chosen method, or null if no method is chosen
257      * @throws ClassNotFoundException
258      */

259     public static JavaClassAndMethod visitSuperInterfaceMethods(
260             JavaClassAndMethod method, JavaClassAndMethodChooser chooser) throws ClassNotFoundException JavaDoc {
261         return findMethod(
262                 method.getJavaClass().getAllInterfaces(),
263                 method.getMethod().getName(),
264                 method.getMethod().getSignature(),
265                 chooser);
266     }
267     
268     /**
269      * Find the least upper bound method in the class hierarchy
270      * which could be called by the given InvokeInstruction.
271      * One reason this method is useful is that it indicates
272      * which declared exceptions are thrown by the called methods.
273      *
274      * <p/>
275      * <ul>
276      * <li> For invokespecial, this is simply an
277      * exact lookup.
278      * <li> For invokestatic and invokevirtual, the named class is searched,
279      * followed by superclasses up to the root of the object
280      * hierarchy (java.lang.Object). Yes, invokestatic really is declared
281      * to check superclasses. See VMSpec, 2nd ed, sec. 5.4.3.3.
282      * <li> For invokeinterface, the named class is searched,
283      * followed by all interfaces transitively declared by the class.
284      * (Question: is the order important here? Maybe the VM spec
285      * requires that the actual interface desired is given,
286      * so the extended lookup will not be required. Should check.)
287      * </ul>
288      *
289      * @param inv the InvokeInstruction
290      * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
291      * @return the JavaClassAndMethod, or null if no matching method can be found
292      */

293     public static JavaClassAndMethod findInvocationLeastUpperBound(
294             InvokeInstruction inv, ConstantPoolGen cpg)
295             throws ClassNotFoundException JavaDoc {
296         return findInvocationLeastUpperBound(inv, cpg, ANY_METHOD);
297     }
298
299     public static JavaClassAndMethod findInvocationLeastUpperBound(
300             InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser methodChooser)
301             throws ClassNotFoundException JavaDoc {
302         JavaClassAndMethod result;
303         
304         if (DEBUG_METHOD_LOOKUP) {
305             System.out.println("Find prototype method for " +
306                     SignatureConverter.convertMethodSignature(inv,cpg));
307         }
308         
309         short opcode = inv.getOpcode();
310         
311         if (methodChooser != ANY_METHOD) {
312             methodChooser = new CompoundMethodChooser(new JavaClassAndMethodChooser[]{
313                     methodChooser, opcode == Constants.INVOKESTATIC ? STATIC_METHOD : INSTANCE_METHOD
314             });
315         }
316
317         // Find the method
318
if (opcode == Constants.INVOKESPECIAL) {
319             // Non-virtual dispatch
320
result = findExactMethod(inv, cpg, methodChooser);
321         } else {
322             String JavaDoc className = inv.getClassName(cpg);
323             String JavaDoc methodName = inv.getName(cpg);
324             String JavaDoc methodSig = inv.getSignature(cpg);
325             if (DEBUG_METHOD_LOOKUP) {
326                 System.out.println("[Class name is " + className + "]");
327                 System.out.println("[Method name is " + methodName + "]");
328                 System.out.println("[Method signature is " + methodSig + "]");
329             }
330             
331             if (className.startsWith("[")) {
332                 // Java 1.5 allows array classes to appear as the class name
333
className= "java.lang.Object";
334             }
335
336             if (opcode == Constants.INVOKEVIRTUAL || opcode == Constants.INVOKESTATIC) {
337                 if (DEBUG_METHOD_LOOKUP) {
338                     System.out.println("[invokevirtual or invokestatic]");
339                 }
340                 // Dispatch where the class hierarchy is searched
341
// Check superclasses
342
result = findMethod(Repository.lookupClass(className), methodName, methodSig, methodChooser);
343                 if (result == null) {
344                     if (DEBUG_METHOD_LOOKUP) {
345                         System.out.println("[not in class, checking superclasses...]");
346                     }
347                     JavaClass[] superClassList = Repository.getSuperClasses(className);
348                     result = findMethod(superClassList, methodName, methodSig, methodChooser);
349                 }
350             } else {
351                 // Check superinterfaces
352
result = findMethod(Repository.lookupClass(className), methodName, methodSig, methodChooser);
353                 if (result == null) {
354                     JavaClass[] interfaceList = Repository.getInterfaces(className);
355                     result = findMethod(interfaceList, methodName, methodSig, methodChooser);
356                 }
357             }
358         }
359
360         return result;
361     }
362
363     /**
364      * Find the declared exceptions for the method called
365      * by given instruction.
366      *
367      * @param inv the InvokeInstruction
368      * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
369      * @return array of ObjectTypes of thrown exceptions, or null
370      * if we can't find the list of declared exceptions
371      */

372     public static ObjectType[] findDeclaredExceptions(InvokeInstruction inv, ConstantPoolGen cpg)
373             throws ClassNotFoundException JavaDoc {
374         JavaClassAndMethod method = findInvocationLeastUpperBound(inv, cpg);
375
376         if (method == null)
377             return null;
378
379         ExceptionTable exTable = method.getMethod().getExceptionTable();
380         if (exTable == null)
381             return new ObjectType[0];
382
383         String JavaDoc[] exNameList = exTable.getExceptionNames();
384         ObjectType[] result = new ObjectType[exNameList.length];
385         for (int i = 0; i < exNameList.length; ++i) {
386             result[i] = ObjectTypeFactory.getInstance(exNameList[i]);
387         }
388         return result;
389     }
390
391     /**
392      * Find a method in given class.
393      *
394      * @param javaClass the class
395      * @param methodName the name of the method
396      * @param methodSig the signature of the method
397      * @return the JavaClassAndMethod, or null if no such method exists in the class
398      */

399     public static JavaClassAndMethod findMethod(JavaClass javaClass, String JavaDoc methodName, String JavaDoc methodSig) {
400         return findMethod(javaClass, methodName, methodSig, ANY_METHOD);
401     }
402
403     /**
404      * Find a method in given class.
405      *
406      * @param javaClass the class
407      * @param methodName the name of the method
408      * @param methodSig the signature of the method
409      * @param chooser JavaClassAndMethodChooser to use to select a matching method
410      * (assuming class, name, and signature already match)
411      * @return the JavaClassAndMethod, or null if no such method exists in the class
412      */

413     public static JavaClassAndMethod findMethod(
414             JavaClass javaClass,
415             String JavaDoc methodName,
416             String JavaDoc methodSig,
417             JavaClassAndMethodChooser chooser) {
418         if (DEBUG_METHOD_LOOKUP) {
419             System.out.println("Check " + javaClass.getClassName());
420         }
421         Method[] methodList = javaClass.getMethods();
422         for (Method method : methodList) {
423             JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(javaClass, method);
424             if (method.getName().equals(methodName)
425                     && method.getSignature().equals(methodSig)
426                     && chooser.choose(javaClassAndMethod)) {
427                 if (DEBUG_METHOD_LOOKUP) {
428                     System.out.println("\t==> FOUND: " + method);
429                 }
430                 return new JavaClassAndMethod(javaClass, method);
431             }
432         }
433         if (DEBUG_METHOD_LOOKUP) {
434             System.out.println("\t==> NOT FOUND");
435         }
436         return null;
437     }
438     
439     /**
440      * Find a method in given class.
441      *
442      * @param javaClass the class
443      * @param methodName the name of the method
444      * @param methodSig the signature of the method
445      * @param chooser the JavaClassAndMethodChooser to use to screen possible candidates
446      * @return the XMethod, or null if no such method exists in the class
447      */

448     public static XMethod findXMethod(JavaClass javaClass, String JavaDoc methodName, String JavaDoc methodSig,
449             JavaClassAndMethodChooser chooser) {
450         JavaClassAndMethod result = findMethod(javaClass, methodName, methodSig, chooser);
451         return result == null ? null : XFactory.createXMethod(result.getJavaClass(), result.getMethod());
452     }
453     
454     /**
455      * JavaClassAndMethodChooser which accepts any method.
456      */

457     public static final JavaClassAndMethodChooser ANY_METHOD = new JavaClassAndMethodChooser() {
458         public boolean choose(JavaClassAndMethod javaClassAndMethod) {
459             return true;
460         }
461     };
462     
463     /**
464      * JavaClassAndMethodChooser which accepts only concrete (not abstract or native) methods.
465      * FIXME: perhaps native methods should be concrete.
466      */

467     public static final JavaClassAndMethodChooser CONCRETE_METHOD = new JavaClassAndMethodChooser() {
468         public boolean choose(JavaClassAndMethod javaClassAndMethod) {
469             Method method = javaClassAndMethod.getMethod();
470             int accessFlags = method.getAccessFlags();
471             return (accessFlags & Constants.ACC_ABSTRACT) == 0
472                 && (accessFlags & Constants.ACC_NATIVE) == 0;
473         }
474     };
475     
476     /**
477      * JavaClassAndMethodChooser which accepts only static methods.
478      */

479     public static final JavaClassAndMethodChooser STATIC_METHOD = new JavaClassAndMethodChooser() {
480         public boolean choose(JavaClassAndMethod javaClassAndMethod) {
481             return javaClassAndMethod.getMethod().isStatic();
482         }
483     };
484     
485     /**
486      * JavaClassAndMethodChooser which accepts only instance methods.
487      */

488     public static final JavaClassAndMethodChooser INSTANCE_METHOD = new JavaClassAndMethodChooser() {
489         public boolean choose(JavaClassAndMethod javaClassAndMethod) {
490             return !javaClassAndMethod.getMethod().isStatic();
491         }
492     };
493
494     /**
495      * Find a method in given list of classes,
496      * searching the classes in order.
497      *
498      * @param classList list of classes in which to search
499      * @param methodName the name of the method
500      * @param methodSig the signature of the method
501      * @return the JavaClassAndMethod, or null if no such method exists in the class
502      */

503     public static JavaClassAndMethod findMethod(JavaClass[] classList, String JavaDoc methodName, String JavaDoc methodSig) {
504         return findMethod(classList, methodName, methodSig, ANY_METHOD);
505     }
506
507     /**
508      * Find a method in given list of classes,
509      * searching the classes in order.
510      *
511      * @param classList list of classes in which to search
512      * @param methodName the name of the method
513      * @param methodSig the signature of the method
514      * @param chooser JavaClassAndMethodChooser to select which methods are considered;
515      * it must return true for a method to be returned
516      * @return the JavaClassAndMethod, or null if no such method exists in the class
517      */

518     public static JavaClassAndMethod findMethod(JavaClass[] classList, String JavaDoc methodName, String JavaDoc methodSig,
519             JavaClassAndMethodChooser chooser) {
520         JavaClassAndMethod m = null;
521
522         for (JavaClass cls : classList) {
523             if ((m = findMethod(cls, methodName, methodSig, chooser)) != null)
524                 break;
525         }
526
527         return m;
528     }
529
530     /**
531      * Find XMethod for method in given list of classes,
532      * searching the classes in order.
533      *
534      * @param classList list of classes in which to search
535      * @param methodName the name of the method
536      * @param methodSig the signature of the method
537      * @return the XMethod, or null if no such method exists in the class
538      */

539     public static XMethod findXMethod(JavaClass[] classList, String JavaDoc methodName, String JavaDoc methodSig) {
540         return findXMethod(classList, methodName, methodSig, ANY_METHOD);
541     }
542
543     /**
544      * Find XMethod for method in given list of classes,
545      * searching the classes in order.
546      *
547      * @param classList list of classes in which to search
548      * @param methodName the name of the method
549      * @param methodSig the signature of the method
550      * @param chooser JavaClassAndMethodChooser to select which methods are considered;
551      * it must return true for a method to be returned
552      * @return the XMethod, or null if no such method exists in the class
553      */

554     public static XMethod findXMethod(JavaClass[] classList, String JavaDoc methodName, String JavaDoc methodSig,
555             JavaClassAndMethodChooser chooser) {
556         for (JavaClass cls : classList) {
557             JavaClassAndMethod m;
558             if ((m = findMethod(cls, methodName, methodSig)) != null && chooser.choose(m)) {
559                 return XFactory.createXMethod(cls, m.getMethod());
560             }
561         }
562         return null;
563     }
564     
565     /**
566      * Resolve possible method call targets.
567      * This works for both static and instance method calls.
568      *
569      * @param invokeInstruction the InvokeInstruction
570      * @param typeFrame the TypeFrame containing the types of stack values
571      * @param cpg the ConstantPoolGen
572      * @return Set of methods which might be called
573      * @throws DataflowAnalysisException
574      * @throws ClassNotFoundException
575      */

576     public static Set JavaDoc<JavaClassAndMethod> resolveMethodCallTargets(
577             InvokeInstruction invokeInstruction,
578             TypeFrame typeFrame,
579             ConstantPoolGen cpg) throws DataflowAnalysisException, ClassNotFoundException JavaDoc {
580         
581         short opcode = invokeInstruction.getOpcode();
582         
583         if (opcode == Constants.INVOKESTATIC) {
584             HashSet JavaDoc<JavaClassAndMethod> result = new HashSet JavaDoc<JavaClassAndMethod>();
585             JavaClassAndMethod targetMethod = findInvocationLeastUpperBound(invokeInstruction, cpg, CONCRETE_METHOD);
586             if (targetMethod != null) {
587                 result.add(targetMethod);
588             }
589             return result;
590         }
591         
592         if (!typeFrame.isValid()) {
593             return new HashSet JavaDoc<JavaClassAndMethod>();
594         }
595
596         Type receiverType;
597         boolean receiverTypeIsExact;
598
599         if (opcode == Constants.INVOKESPECIAL) {
600             // invokespecial instructions are dispatched to EXACTLY
601
// the class specified by the instruction
602
receiverType = ObjectTypeFactory.getInstance(invokeInstruction.getClassName(cpg));
603             receiverTypeIsExact = false; // Doesn't actually matter
604
} else {
605             // For invokevirtual and invokeinterface instructions, we have
606
// virtual dispatch. By taking the receiver type (which may be a
607
// subtype of the class specified by the instruction),
608
// we may get a more precise set of call targets.
609
int instanceStackLocation = typeFrame.getInstanceStackLocation(invokeInstruction, cpg);
610             receiverType = typeFrame.getStackValue(instanceStackLocation);
611             if (!(receiverType instanceof ReferenceType)) {
612                 return new HashSet JavaDoc<JavaClassAndMethod>();
613             }
614             receiverTypeIsExact = typeFrame.isExact(instanceStackLocation);
615         }
616         if (DEBUG_METHOD_LOOKUP) {
617             System.out.println("[receiver type is " + receiverType + ", " +
618                     (receiverTypeIsExact ? "exact]" : " not exact]"));
619         }
620
621         return resolveMethodCallTargets((ReferenceType) receiverType, invokeInstruction, cpg, receiverTypeIsExact);
622     }
623     
624     /**
625      * Resolve possible instance method call targets.
626      * Assumes that invokevirtual and invokeinterface methods may
627      * call any subtype of the receiver class.
628      *
629      * @param receiverType type of the receiver object
630      * @param invokeInstruction the InvokeInstruction
631      * @param cpg the ConstantPoolGen
632      * @return Set of methods which might be called
633      * @throws ClassNotFoundException
634      */

635     public static Set JavaDoc<JavaClassAndMethod> resolveMethodCallTargets(
636             ReferenceType receiverType,
637             InvokeInstruction invokeInstruction,
638             ConstantPoolGen cpg
639             ) throws ClassNotFoundException JavaDoc {
640         return resolveMethodCallTargets(receiverType, invokeInstruction, cpg, false);
641     }
642
643     /**
644      * Resolve possible instance method call targets.
645      *
646      * @param receiverType type of the receiver object
647      * @param invokeInstruction the InvokeInstruction
648      * @param cpg the ConstantPoolGen
649      * @param receiverTypeIsExact if true, the receiver type is known exactly,
650      * which should allow a precise result
651      * @return Set of methods which might be called
652      * @throws ClassNotFoundException
653      */

654     public static Set JavaDoc<JavaClassAndMethod> resolveMethodCallTargets(
655             ReferenceType receiverType,
656             InvokeInstruction invokeInstruction,
657             ConstantPoolGen cpg,
658             boolean receiverTypeIsExact
659             ) throws ClassNotFoundException JavaDoc {
660         HashSet JavaDoc<JavaClassAndMethod> result = new HashSet JavaDoc<JavaClassAndMethod>();
661         
662         if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC)
663             throw new IllegalArgumentException JavaDoc();
664         
665         String JavaDoc methodName = invokeInstruction.getName(cpg);
666         String JavaDoc methodSig = invokeInstruction.getSignature(cpg);
667         
668         // Array method calls aren't virtual.
669
// They should just resolve to Object methods.
670
if (receiverType instanceof ArrayType) {
671             JavaClass javaLangObject = AnalysisContext.currentAnalysisContext().lookupClass("java.lang.Object");
672             JavaClassAndMethod classAndMethod = findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD);
673             if (classAndMethod != null)
674                 result.add(classAndMethod);
675             return result;
676         }
677         
678         AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
679         
680         // Get the receiver class.
681
JavaClass receiverClass = analysisContext.lookupClass(
682                 ((ObjectType) receiverType).getClassName());
683
684         // Figure out the upper bound for the method.
685
// This is what will be called if this is not a virtual call site.
686
JavaClassAndMethod upperBound = findMethod(receiverClass, methodName, methodSig, CONCRETE_METHOD);
687         if (upperBound == null) {
688             // Try superclasses
689
JavaClass[] superClassList = receiverClass.getSuperClasses();
690             upperBound = findMethod(superClassList, methodName, methodSig, CONCRETE_METHOD);
691         }
692         if (upperBound != null) {
693             if (DEBUG_METHOD_LOOKUP) {
694                 System.out.println("Adding upper bound: " +
695                         SignatureConverter.convertMethodSignature(upperBound.getJavaClass(), upperBound.getMethod()));
696             }
697             result.add(upperBound);
698         }
699         
700         // Is this a virtual call site?
701
boolean virtualCall =
702                invokeInstruction.getOpcode() != Constants.INVOKESPECIAL
703             && !receiverTypeIsExact;
704         
705         if (virtualCall) {
706             // This is a true virtual call: assume that any concrete
707
// subtype method may be called.
708
Set JavaDoc<JavaClass> subTypeSet = analysisContext.getSubtypes().getTransitiveSubtypes(receiverClass);
709             for (JavaClass subtype : subTypeSet) {
710                 JavaClassAndMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, CONCRETE_METHOD);
711                 if (concreteSubtypeMethod != null) {
712                     result.add(concreteSubtypeMethod);
713                 }
714             }
715         }
716         
717         return result;
718     }
719 //
720
// /**
721
// * Return whether or not the given method is concrete.
722
// *
723
// * @param xmethod the method
724
// * @return true if the method is concrete, false otherwise
725
// */
726
// public static boolean isConcrete(XMethod xmethod) {
727
// int accessFlags = xmethod.getAccessFlags();
728
// return (accessFlags & Constants.ACC_ABSTRACT) == 0
729
// && (accessFlags & Constants.ACC_NATIVE) == 0;
730
// }
731

732     /**
733      * Find a field with given name defined in given class.
734      *
735      * @param className the name of the class
736      * @param fieldName the name of the field
737      * @return the Field, or null if no such field could be found
738      */

739     public static Field findField(String JavaDoc className, String JavaDoc fieldName) throws ClassNotFoundException JavaDoc {
740         JavaClass jclass = Repository.lookupClass(className);
741
742         while (jclass != null) {
743             Field[] fieldList = jclass.getFields();
744             for (Field field : fieldList) {
745                 if (field.getName().equals(fieldName)) {
746                     return field;
747                 }
748             }
749
750             jclass = jclass.getSuperClass();
751         }
752
753         return null;
754     }
755
756 /*
757     public static JavaClass findClassDefiningField(String className, String fieldName, String fieldSig)
758         throws ClassNotFoundException {
759
760         JavaClass jclass = Repository.lookupClass(className);
761
762         while (jclass != null) {
763             Field[] fieldList = jclass.getFields();
764             for (int i = 0; i < fieldList.length; ++i) {
765                 Field field = fieldList[i];
766                 if (field.getName().equals(fieldName) && field.getSignature().equals(fieldSig)) {
767                     return jclass;
768                 }
769             }
770     
771             jclass = jclass.getSuperClass();
772         }
773
774         return null;
775     }
776 */

777
778     /**
779      * Look up a field with given name and signature in given class,
780      * returning it as an {@link XField XField} object.
781      * If a field can't be found in the immediate class,
782      * its superclass is search, and so forth.
783      *
784      * @param className name of the class through which the field
785      * is referenced
786      * @param fieldName name of the field
787      * @param fieldSig signature of the field
788      * @return an XField object representing the field, or null if no such field could be found
789      */

790     public static XField findXField(String JavaDoc className, String JavaDoc fieldName, String JavaDoc fieldSig)
791             throws ClassNotFoundException JavaDoc {
792
793         JavaClass classDefiningField = Repository.lookupClass(className);
794
795         Field field = null;
796         loop:
797             while (classDefiningField != null) {
798                 Field[] fieldList = classDefiningField.getFields();
799                 for (Field aFieldList : fieldList) {
800                     field = aFieldList;
801                     if (field.getName().equals(fieldName) && field.getSignature().equals(fieldSig)) {
802                         break loop;
803                     }
804                 }
805
806                 classDefiningField = classDefiningField.getSuperClass();
807             }
808
809         if (classDefiningField == null)
810             return null;
811         else {
812             String JavaDoc realClassName = classDefiningField.getClassName();
813             int accessFlags = field.getAccessFlags();
814             return field.isStatic()
815                     ? (XField) new StaticField(realClassName, fieldName, fieldSig, accessFlags)
816                     : (XField) new InstanceField(realClassName, fieldName, fieldSig, accessFlags);
817         }
818     }
819
820     /**
821      * Look up the field referenced by given FieldInstruction,
822      * returning it as an {@link XField XField} object.
823      *
824      * @param fins the FieldInstruction
825      * @param cpg the ConstantPoolGen used by the class containing the instruction
826      * @return an XField object representing the field, or null
827      * if no such field could be found
828      */

829     public static XField findXField(FieldInstruction fins, @NonNull ConstantPoolGen cpg)
830             throws ClassNotFoundException JavaDoc {
831
832         String JavaDoc className = fins.getClassName(cpg);
833         String JavaDoc fieldName = fins.getFieldName(cpg);
834         String JavaDoc fieldSig = fins.getSignature(cpg);
835
836         XField xfield = findXField(className, fieldName, fieldSig);
837         short opcode = fins.getOpcode();
838         if (xfield != null &&
839                 xfield.isStatic() == (opcode == Constants.GETSTATIC || opcode == Constants.PUTSTATIC))
840             return xfield;
841         else
842             return null;
843     }
844
845     /**
846      * Determine whether the given INVOKESTATIC instruction
847      * is an inner-class field accessor method.
848      * @param inv the INVOKESTATIC instruction
849      * @param cpg the ConstantPoolGen for the method
850      * @return true if the instruction is an inner-class field accessor, false if not
851      */

852     public static boolean isInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) {
853         String JavaDoc methodName = inv.getName(cpg);
854         return methodName.startsWith("access$");
855     }
856
857     /**
858      * Get the InnerClassAccess for access method called
859      * by given INVOKESTATIC.
860      * @param inv the INVOKESTATIC instruction
861      * @param cpg the ConstantPoolGen for the method
862      * @return the InnerClassAccess, or null if the instruction is not
863      * an inner-class access
864      */

865     public static InnerClassAccess getInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg)
866             throws ClassNotFoundException JavaDoc {
867
868         String JavaDoc className = inv.getClassName(cpg);
869         String JavaDoc methodName = inv.getName(cpg);
870         String JavaDoc methodSig = inv.getSignature(cpg);
871
872         InnerClassAccess access = AnalysisContext.currentAnalysisContext()
873             .getInnerClassAccessMap().getInnerClassAccess(className, methodName);
874         return (access != null && access.getMethodSignature().equals(methodSig))
875             ? access
876             : null;
877     }
878 }
879
880 // vim:ts=4
881
Popular Tags