0001: /*
0002: * Bytecode Analysis Framework
0003: * Copyright (C) 2003,2004 University of Maryland
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public
0007: * License as published by the Free Software Foundation; either
0008: * version 2.1 of the License, or (at your option) any later version.
0009: *
0010: * This library is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * Lesser General Public License for more details.
0014: *
0015: * You should have received a copy of the GNU Lesser General Public
0016: * License along with this library; if not, write to the Free Software
0017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0018: */
0019:
0020: package edu.umd.cs.findbugs.ba;
0021:
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Map;
0025: import java.util.Set;
0026:
0027: import org.apache.bcel.Constants;
0028: import org.apache.bcel.Repository;
0029: import org.apache.bcel.classfile.Field;
0030: import org.apache.bcel.classfile.JavaClass;
0031: import org.apache.bcel.classfile.Method;
0032: import org.apache.bcel.generic.ArrayType;
0033: import org.apache.bcel.generic.ConstantPoolGen;
0034: import org.apache.bcel.generic.FieldInstruction;
0035: import org.apache.bcel.generic.INVOKESTATIC;
0036: import org.apache.bcel.generic.Instruction;
0037: import org.apache.bcel.generic.InvokeInstruction;
0038: import org.apache.bcel.generic.ObjectType;
0039: import org.apache.bcel.generic.ReferenceType;
0040: import org.apache.bcel.generic.Type;
0041:
0042: import edu.umd.cs.findbugs.SystemProperties;
0043: import edu.umd.cs.findbugs.annotations.CheckForNull;
0044: import edu.umd.cs.findbugs.annotations.NonNull;
0045: import edu.umd.cs.findbugs.ba.ch.Subtypes2;
0046: import edu.umd.cs.findbugs.ba.type.TypeFrame;
0047: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
0048: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
0049: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
0050: import edu.umd.cs.findbugs.classfile.Global;
0051:
0052: /**
0053: * Facade for class hierarchy queries.
0054: * These typically access the class hierarchy using
0055: * the {@link org.apache.bcel.Repository} class. Callers should generally
0056: * expect to handle ClassNotFoundException for when referenced
0057: * classes can't be found.
0058: *
0059: * @author David Hovemeyer
0060: */
0061: public class Hierarchy {
0062: protected static final boolean DEBUG_METHOD_LOOKUP = SystemProperties
0063: .getBoolean("hier.lookup.debug");
0064:
0065: /**
0066: * Type of java.lang.Exception.
0067: */
0068: public static final ObjectType EXCEPTION_TYPE = ObjectTypeFactory
0069: .getInstance("java.lang.Exception");
0070: /**
0071: * Type of java.lang.Error.
0072: */
0073: public static final ObjectType ERROR_TYPE = ObjectTypeFactory
0074: .getInstance("java.lang.Error");
0075: /**
0076: * Type of java.lang.RuntimeException.
0077: */
0078: public static final ObjectType RUNTIME_EXCEPTION_TYPE = ObjectTypeFactory
0079: .getInstance("java.lang.RuntimeException");
0080:
0081: /**
0082: * Determine whether one class (or reference type) is a subtype
0083: * of another.
0084: *
0085: * @param clsName the name of the class or reference type
0086: * @param possibleSupertypeClassName the name of the possible superclass
0087: * @return true if clsName is a subtype of possibleSupertypeClassName,
0088: * false if not
0089: */
0090: public static boolean isSubtype(String clsName,
0091: String possibleSupertypeClassName)
0092: throws ClassNotFoundException {
0093: ObjectType cls = ObjectTypeFactory.getInstance(clsName);
0094: ObjectType super Cls = ObjectTypeFactory
0095: .getInstance(possibleSupertypeClassName);
0096: return isSubtype(cls, super Cls);
0097: }
0098:
0099: /**
0100: * Determine if one reference type is a subtype of another.
0101: *
0102: * @param t a reference type
0103: * @param possibleSupertype the possible supertype
0104: * @return true if t is a subtype of possibleSupertype,
0105: * false if not
0106: */
0107: public static boolean isSubtype(ReferenceType t,
0108: ReferenceType possibleSupertype)
0109: throws ClassNotFoundException {
0110: if (Subtypes2.ENABLE_SUBTYPES2) {
0111: try {
0112: Subtypes2 subtypes2 = Global.getAnalysisCache()
0113: .getDatabase(Subtypes2.class);
0114: return subtypes2.isSubtype(t, possibleSupertype);
0115: } catch (CheckedAnalysisException e) {
0116: // Should not happen
0117: IllegalStateException ise = new IllegalStateException(
0118: "Should not happen");
0119: ise.initCause(e);
0120: throw ise;
0121: }
0122: } else {
0123: Map<ReferenceType, Boolean> subtypes = subtypeCache
0124: .get(possibleSupertype);
0125: if (subtypes == null) {
0126: subtypes = new HashMap<ReferenceType, Boolean>();
0127: subtypeCache.put(possibleSupertype, subtypes);
0128: }
0129: Boolean result = subtypes.get(t);
0130: if (result == null) {
0131: result = Boolean.valueOf(t
0132: .isAssignmentCompatibleWith(possibleSupertype));
0133: subtypes.put(t, result);
0134: }
0135: return result;
0136: }
0137: }
0138:
0139: static Map<ReferenceType, Map<ReferenceType, Boolean>> subtypeCache = new HashMap<ReferenceType, Map<ReferenceType, Boolean>>();
0140:
0141: /**
0142: * Determine if the given ObjectType reference represents
0143: * a <em>universal</em> exception handler. That is,
0144: * one that will catch any kind of exception.
0145: *
0146: * @param catchType the ObjectType of the exception handler
0147: * @return true if catchType is null, or if catchType is
0148: * java.lang.Throwable
0149: */
0150: public static boolean isUniversalExceptionHandler(
0151: ObjectType catchType) {
0152: return catchType == null || catchType.equals(Type.THROWABLE);
0153: }
0154:
0155: /**
0156: * Determine if the given ObjectType refers to an unchecked
0157: * exception (RuntimeException or Error).
0158: */
0159: public static boolean isUncheckedException(ObjectType type)
0160: throws ClassNotFoundException {
0161: return isSubtype(type, RUNTIME_EXCEPTION_TYPE)
0162: || isSubtype(type, ERROR_TYPE);
0163: }
0164:
0165: /**
0166: * Determine if method whose name and signature is specified
0167: * is a monitor wait operation.
0168: *
0169: * @param methodName name of the method
0170: * @param methodSig signature of the method
0171: * @return true if the method is a monitor wait, false if not
0172: */
0173: public static boolean isMonitorWait(String methodName,
0174: String methodSig) {
0175: return methodName.equals("wait")
0176: && (methodSig.equals("()V") || methodSig.equals("(J)V") || methodSig
0177: .equals("(JI)V"));
0178: }
0179:
0180: /**
0181: * Determine if given Instruction is a monitor wait.
0182: *
0183: * @param ins the Instruction
0184: * @param cpg the ConstantPoolGen for the Instruction
0185: *
0186: * @return true if the instruction is a monitor wait, false if not
0187: */
0188: public static boolean isMonitorWait(Instruction ins,
0189: ConstantPoolGen cpg) {
0190: if (!(ins instanceof InvokeInstruction))
0191: return false;
0192: if (ins.getOpcode() == Constants.INVOKESTATIC)
0193: return false;
0194:
0195: InvokeInstruction inv = (InvokeInstruction) ins;
0196: String methodName = inv.getMethodName(cpg);
0197: String methodSig = inv.getSignature(cpg);
0198:
0199: return isMonitorWait(methodName, methodSig);
0200: }
0201:
0202: /**
0203: * Determine if method whose name and signature is specified
0204: * is a monitor notify operation.
0205: *
0206: * @param methodName name of the method
0207: * @param methodSig signature of the method
0208: * @return true if the method is a monitor notify, false if not
0209: */
0210: public static boolean isMonitorNotify(String methodName,
0211: String methodSig) {
0212: return (methodName.equals("notify") || methodName
0213: .equals("notifyAll"))
0214: && methodSig.equals("()V");
0215: }
0216:
0217: /**
0218: * Determine if given Instruction is a monitor wait.
0219: *
0220: * @param ins the Instruction
0221: * @param cpg the ConstantPoolGen for the Instruction
0222: *
0223: * @return true if the instruction is a monitor wait, false if not
0224: */
0225: public static boolean isMonitorNotify(Instruction ins,
0226: ConstantPoolGen cpg) {
0227: if (!(ins instanceof InvokeInstruction))
0228: return false;
0229: if (ins.getOpcode() == Constants.INVOKESTATIC)
0230: return false;
0231:
0232: InvokeInstruction inv = (InvokeInstruction) ins;
0233: String methodName = inv.getMethodName(cpg);
0234: String methodSig = inv.getSignature(cpg);
0235:
0236: return isMonitorNotify(methodName, methodSig);
0237: }
0238:
0239: /**
0240: * Look up the method referenced by given InvokeInstruction.
0241: * This method does <em>not</em> look for implementations in
0242: * super or subclasses according to the virtual dispatch rules.
0243: *
0244: * @param inv the InvokeInstruction
0245: * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
0246: * @return the JavaClassAndMethod, or null if no such method is defined in the class
0247: */
0248: public static JavaClassAndMethod findExactMethod(
0249: InvokeInstruction inv, ConstantPoolGen cpg)
0250: throws ClassNotFoundException {
0251: return findExactMethod(inv, cpg, ANY_METHOD);
0252: }
0253:
0254: /**
0255: * Look up the method referenced by given InvokeInstruction.
0256: * This method does <em>not</em> look for implementations in
0257: * super or subclasses according to the virtual dispatch rules.
0258: *
0259: * @param inv the InvokeInstruction
0260: * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
0261: * @param chooser JavaClassAndMethodChooser to use to pick the method from among the candidates
0262: * @return the JavaClassAndMethod, or null if no such method is defined in the class
0263: */
0264: public static JavaClassAndMethod findExactMethod(
0265: InvokeInstruction inv, ConstantPoolGen cpg,
0266: JavaClassAndMethodChooser chooser)
0267: throws ClassNotFoundException {
0268: String className = inv.getClassName(cpg);
0269: String methodName = inv.getName(cpg);
0270: String methodSig = inv.getSignature(cpg);
0271:
0272: JavaClass jclass = Repository.lookupClass(className);
0273: return findMethod(jclass, methodName, methodSig, chooser);
0274: }
0275:
0276: /**
0277: * Visit all superclass methods which the given method overrides.
0278: *
0279: * @param method the method
0280: * @param chooser chooser which visits each superclass method
0281: * @return the chosen method, or null if no method is chosen
0282: * @throws ClassNotFoundException
0283: */
0284: public static JavaClassAndMethod visitSuperClassMethods(
0285: JavaClassAndMethod method, JavaClassAndMethodChooser chooser)
0286: throws ClassNotFoundException {
0287: return findMethod(method.getJavaClass().getSuperClasses(),
0288: method.getMethod().getName(), method.getMethod()
0289: .getSignature(), chooser);
0290: }
0291:
0292: /**
0293: * Visit all superinterface methods which the given method implements.
0294: *
0295: * @param method the method
0296: * @param chooser chooser which visits each superinterface method
0297: * @return the chosen method, or null if no method is chosen
0298: * @throws ClassNotFoundException
0299: */
0300: public static JavaClassAndMethod visitSuperInterfaceMethods(
0301: JavaClassAndMethod method, JavaClassAndMethodChooser chooser)
0302: throws ClassNotFoundException {
0303: return findMethod(method.getJavaClass().getAllInterfaces(),
0304: method.getMethod().getName(), method.getMethod()
0305: .getSignature(), chooser);
0306: }
0307:
0308: /**
0309: * Find the least upper bound method in the class hierarchy
0310: * which could be called by the given InvokeInstruction.
0311: * One reason this method is useful is that it indicates
0312: * which declared exceptions are thrown by the called methods.
0313: *
0314: * <p/>
0315: * <ul>
0316: * <li> For invokespecial, this is simply an
0317: * exact lookup.
0318: * <li> For invokestatic and invokevirtual, the named class is searched,
0319: * followed by superclasses up to the root of the object
0320: * hierarchy (java.lang.Object). Yes, invokestatic really is declared
0321: * to check superclasses. See VMSpec, 2nd ed, sec. 5.4.3.3.
0322: * <li> For invokeinterface, the named class is searched,
0323: * followed by all interfaces transitively declared by the class.
0324: * (Question: is the order important here? Maybe the VM spec
0325: * requires that the actual interface desired is given,
0326: * so the extended lookup will not be required. Should check.)
0327: * </ul>
0328: *
0329: * @param inv the InvokeInstruction
0330: * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
0331: * @return the JavaClassAndMethod, or null if no matching method can be found
0332: */
0333: public static @CheckForNull
0334: JavaClassAndMethod findInvocationLeastUpperBound(
0335: InvokeInstruction inv, ConstantPoolGen cpg)
0336: throws ClassNotFoundException {
0337: return findInvocationLeastUpperBound(inv, cpg, ANY_METHOD);
0338: }
0339:
0340: public static @CheckForNull
0341: JavaClassAndMethod findInvocationLeastUpperBound(
0342: InvokeInstruction inv, ConstantPoolGen cpg,
0343: JavaClassAndMethodChooser methodChooser)
0344: throws ClassNotFoundException {
0345:
0346: if (DEBUG_METHOD_LOOKUP) {
0347: System.out.println("Find prototype method for "
0348: + SignatureConverter.convertMethodSignature(inv,
0349: cpg));
0350: }
0351:
0352: short opcode = inv.getOpcode();
0353:
0354: if (opcode == Constants.INVOKESTATIC) {
0355: if (methodChooser == INSTANCE_METHOD)
0356: return null;
0357: } else {
0358: if (methodChooser == STATIC_METHOD)
0359: return null;
0360: }
0361:
0362: // Find the method
0363: if (opcode == Constants.INVOKESPECIAL) {
0364: // Non-virtual dispatch
0365: return findExactMethod(inv, cpg, methodChooser);
0366: } else {
0367: String className = inv.getClassName(cpg);
0368: String methodName = inv.getName(cpg);
0369: String methodSig = inv.getSignature(cpg);
0370: if (DEBUG_METHOD_LOOKUP) {
0371: System.out.println("[Class name is " + className + "]");
0372: System.out.println("[Method name is " + methodName
0373: + "]");
0374: System.out.println("[Method signature is " + methodSig
0375: + "]");
0376: }
0377:
0378: if (className.startsWith("[")) {
0379: // Java 1.5 allows array classes to appear as the class name
0380: className = "java.lang.Object";
0381: }
0382:
0383: JavaClass jClass = Repository.lookupClass(className);
0384: return findInvocationLeastUpperBound(jClass, methodName,
0385: methodSig, methodChooser,
0386: opcode == Constants.INVOKEINTERFACE);
0387:
0388: }
0389: }
0390:
0391: public static @CheckForNull
0392: JavaClassAndMethod findInvocationLeastUpperBound(JavaClass jClass,
0393: String methodName, String methodSig,
0394: JavaClassAndMethodChooser methodChooser,
0395: boolean invokeInterface) throws ClassNotFoundException {
0396: JavaClassAndMethod result = findMethod(jClass, methodName,
0397: methodSig, methodChooser);
0398: if (result != null)
0399: return result;
0400: if (invokeInterface)
0401: for (JavaClass i : jClass.getInterfaces()) {
0402: result = findInvocationLeastUpperBound(i, methodName,
0403: methodSig, methodChooser, invokeInterface);
0404: if (result != null)
0405: return null;
0406: }
0407: else {
0408: JavaClass sClass = jClass.getSuperClass();
0409: if (sClass != null)
0410: return findInvocationLeastUpperBound(sClass,
0411: methodName, methodSig, methodChooser,
0412: invokeInterface);
0413: }
0414: return null;
0415:
0416: }
0417:
0418: /**
0419: * Find the declared exceptions for the method called
0420: * by given instruction.
0421: *
0422: * @param inv the InvokeInstruction
0423: * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to
0424: * @return array of ObjectTypes of thrown exceptions, or null
0425: * if we can't find the list of declared exceptions
0426: * @deprecated Use {@link Hierarchy2#findDeclaredExceptions(InvokeInstruction,ConstantPoolGen)} instead
0427: */
0428: public static ObjectType[] findDeclaredExceptions(
0429: InvokeInstruction inv, ConstantPoolGen cpg)
0430: throws ClassNotFoundException {
0431: return Hierarchy2.findDeclaredExceptions(inv, cpg);
0432: }
0433:
0434: /**
0435: * Find a method in given class.
0436: *
0437: * @param javaClass the class
0438: * @param methodName the name of the method
0439: * @param methodSig the signature of the method
0440: * @return the JavaClassAndMethod, or null if no such method exists in the class
0441: */
0442: public static @CheckForNull
0443: JavaClassAndMethod findMethod(JavaClass javaClass,
0444: String methodName, String methodSig) {
0445: return findMethod(javaClass, methodName, methodSig, ANY_METHOD);
0446: }
0447:
0448: public static @CheckForNull
0449: JavaClassAndMethod findMethod(JavaClass javaClass,
0450: String methodName, String methodSig,
0451: JavaClassAndMethodChooser chooser) {
0452: if (DEBUG_METHOD_LOOKUP) {
0453: System.out.println("Check " + javaClass.getClassName());
0454: }
0455: Method[] methodList = javaClass.getMethods();
0456: for (Method method : methodList)
0457: if (method.getName().equals(methodName)
0458: && method.getSignature().equals(methodSig)) {
0459: JavaClassAndMethod m = new JavaClassAndMethod(
0460: javaClass, method);
0461: if (chooser.choose(m)) {
0462: if (DEBUG_METHOD_LOOKUP) {
0463: System.out.println("\t==> FOUND: " + method);
0464: }
0465: return m;
0466: }
0467: }
0468: if (DEBUG_METHOD_LOOKUP) {
0469: System.out.println("\t==> NOT FOUND");
0470: }
0471: return null;
0472: }
0473:
0474: /**
0475: * Find a method in given class.
0476: *
0477: * @param classDesc the class descriptor
0478: * @param methodName the name of the method
0479: * @param methodSig the signature of the method
0480: * @param isStatic are we looking for a static method?
0481: * @return the JavaClassAndMethod, or null if no such method exists in the class
0482: */
0483: public static @CheckForNull
0484: XMethod findMethod(ClassDescriptor classDesc, String methodName,
0485: String methodSig, boolean isStatic) {
0486: if (DEBUG_METHOD_LOOKUP) {
0487: System.out.println("Check " + classDesc.getClassName());
0488: }
0489:
0490: try {
0491: XClass xClass = Global.getAnalysisCache().getClassAnalysis(
0492: XClass.class, classDesc);
0493: return xClass.findMethod(methodName, methodSig, isStatic);
0494: } catch (CheckedAnalysisException e) {
0495: AnalysisContext.logError("Error looking for " + classDesc
0496: + "." + methodName + methodSig, e);
0497: return null;
0498: }
0499:
0500: }
0501:
0502: /**
0503: * Find a method in given class.
0504: *
0505: * @param javaClass the class
0506: * @param methodName the name of the method
0507: * @param methodSig the signature of the method
0508: * @return the JavaClassAndMethod, or null if no such method exists in the class
0509: */
0510: @Deprecated
0511: public static @CheckForNull
0512: JavaClassAndMethod findConcreteMethod(JavaClass javaClass,
0513: String methodName, String methodSig) {
0514:
0515: if (DEBUG_METHOD_LOOKUP) {
0516: System.out.println("Check " + javaClass.getClassName());
0517: }
0518: Method[] methodList = javaClass.getMethods();
0519: for (Method method : methodList)
0520: if (method.getName().equals(methodName)
0521: && method.getSignature().equals(methodSig)
0522: && accessFlagsAreConcrete(method.getAccessFlags())) {
0523: JavaClassAndMethod m = new JavaClassAndMethod(
0524: javaClass, method);
0525:
0526: return m;
0527:
0528: }
0529: if (DEBUG_METHOD_LOOKUP) {
0530: System.out.println("\t==> NOT FOUND");
0531: }
0532: return null;
0533: }
0534:
0535: /**
0536: * Find a method in given class.
0537: *
0538: * @param javaClass the class
0539: * @param methodName the name of the method
0540: * @param methodSig the signature of the method
0541: * @param chooser the JavaClassAndMethodChooser to use to screen possible candidates
0542: * @return the XMethod, or null if no such method exists in the class
0543: */
0544: @Deprecated
0545: public static @CheckForNull
0546: XMethod findXMethod(JavaClass javaClass, String methodName,
0547: String methodSig, JavaClassAndMethodChooser chooser) {
0548: JavaClassAndMethod result = findMethod(javaClass, methodName,
0549: methodSig, chooser);
0550: return result == null ? null : XFactory.createXMethod(result
0551: .getJavaClass(), result.getMethod());
0552: }
0553:
0554: /**
0555: * JavaClassAndMethodChooser which accepts any method.
0556: */
0557: public static final JavaClassAndMethodChooser ANY_METHOD = new JavaClassAndMethodChooser() {
0558: public boolean choose(JavaClassAndMethod javaClassAndMethod) {
0559: return true;
0560: }
0561:
0562: public boolean choose(XMethod method) {
0563: return true;
0564: }
0565:
0566: };
0567:
0568: // FIXME: perhaps native methods should be concrete.
0569: public static boolean accessFlagsAreConcrete(int accessFlags) {
0570: return (accessFlags & Constants.ACC_ABSTRACT) == 0
0571: && (accessFlags & Constants.ACC_NATIVE) == 0;
0572: }
0573:
0574: /**
0575: * JavaClassAndMethodChooser which accepts only concrete (not abstract or native) methods.
0576: */
0577: public static final JavaClassAndMethodChooser CONCRETE_METHOD = new JavaClassAndMethodChooser() {
0578: public boolean choose(JavaClassAndMethod javaClassAndMethod) {
0579: Method method = javaClassAndMethod.getMethod();
0580: int accessFlags = method.getAccessFlags();
0581: return accessFlagsAreConcrete(accessFlags);
0582: }
0583:
0584: public boolean choose(XMethod method) {
0585: return accessFlagsAreConcrete(method.getAccessFlags());
0586: }
0587: };
0588:
0589: /**
0590: * JavaClassAndMethodChooser which accepts only static methods.
0591: */
0592: public static final JavaClassAndMethodChooser STATIC_METHOD = new JavaClassAndMethodChooser() {
0593: public boolean choose(JavaClassAndMethod javaClassAndMethod) {
0594: return javaClassAndMethod.getMethod().isStatic();
0595: }
0596:
0597: public boolean choose(XMethod method) {
0598: return method.isStatic();
0599: }
0600: };
0601:
0602: /**
0603: * JavaClassAndMethodChooser which accepts only instance methods.
0604: */
0605: public static final JavaClassAndMethodChooser INSTANCE_METHOD = new JavaClassAndMethodChooser() {
0606: public boolean choose(JavaClassAndMethod javaClassAndMethod) {
0607: return !javaClassAndMethod.getMethod().isStatic();
0608: }
0609:
0610: public boolean choose(XMethod method) {
0611: return !method.isStatic();
0612: }
0613: };
0614:
0615: /**
0616: * Find a method in given list of classes,
0617: * searching the classes in order.
0618: *
0619: * @param classList list of classes in which to search
0620: * @param methodName the name of the method
0621: * @param methodSig the signature of the method
0622: * @return the JavaClassAndMethod, or null if no such method exists in the class
0623: */
0624: @Deprecated
0625: public static JavaClassAndMethod findMethod(JavaClass[] classList,
0626: String methodName, String methodSig) {
0627: return findMethod(classList, methodName, methodSig, ANY_METHOD);
0628: }
0629:
0630: /**
0631: * Find a method in given list of classes,
0632: * searching the classes in order.
0633: *
0634: * @param classList list of classes in which to search
0635: * @param methodName the name of the method
0636: * @param methodSig the signature of the method
0637: * @param chooser JavaClassAndMethodChooser to select which methods are considered;
0638: * it must return true for a method to be returned
0639: * @return the JavaClassAndMethod, or null if no such method exists in the class
0640: */
0641: public static JavaClassAndMethod findMethod(JavaClass[] classList,
0642: String methodName, String methodSig,
0643: JavaClassAndMethodChooser chooser) {
0644: JavaClassAndMethod m = null;
0645:
0646: for (JavaClass cls : classList) {
0647: if ((m = findMethod(cls, methodName, methodSig, chooser)) != null)
0648: break;
0649: }
0650:
0651: return m;
0652: }
0653:
0654: /**
0655: * Find XMethod for method in given list of classes,
0656: * searching the classes in order.
0657: *
0658: * @param classList list of classes in which to search
0659: * @param methodName the name of the method
0660: * @param methodSig the signature of the method
0661: * @return the XMethod, or null if no such method exists in the class
0662: */
0663: @Deprecated
0664: public static XMethod findXMethod(JavaClass[] classList,
0665: String methodName, String methodSig) {
0666: return findXMethod(classList, methodName, methodSig, ANY_METHOD);
0667: }
0668:
0669: /**
0670: * Find XMethod for method in given list of classes,
0671: * searching the classes in order.
0672: *
0673: * @param classList list of classes in which to search
0674: * @param methodName the name of the method
0675: * @param methodSig the signature of the method
0676: * @param chooser JavaClassAndMethodChooser to select which methods are considered;
0677: * it must return true for a method to be returned
0678: * @return the XMethod, or null if no such method exists in the class
0679: */
0680: @Deprecated
0681: public static XMethod findXMethod(JavaClass[] classList,
0682: String methodName, String methodSig,
0683: JavaClassAndMethodChooser chooser) {
0684: for (JavaClass cls : classList) {
0685: JavaClassAndMethod m;
0686: if ((m = findMethod(cls, methodName, methodSig)) != null
0687: && chooser.choose(m)) {
0688: return XFactory.createXMethod(cls, m.getMethod());
0689: }
0690: }
0691: return null;
0692: }
0693:
0694: /**
0695: * Resolve possible method call targets.
0696: * This works for both static and instance method calls.
0697: *
0698: * @param invokeInstruction the InvokeInstruction
0699: * @param typeFrame the TypeFrame containing the types of stack values
0700: * @param cpg the ConstantPoolGen
0701: * @return Set of methods which might be called
0702: * @throws DataflowAnalysisException
0703: * @throws ClassNotFoundException
0704: */
0705: public static Set<JavaClassAndMethod> resolveMethodCallTargets(
0706: InvokeInstruction invokeInstruction, TypeFrame typeFrame,
0707: ConstantPoolGen cpg) throws DataflowAnalysisException,
0708: ClassNotFoundException {
0709:
0710: short opcode = invokeInstruction.getOpcode();
0711:
0712: if (opcode == Constants.INVOKESTATIC) {
0713: HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>();
0714: JavaClassAndMethod targetMethod = findInvocationLeastUpperBound(
0715: invokeInstruction, cpg, CONCRETE_METHOD);
0716: if (targetMethod != null) {
0717: result.add(targetMethod);
0718: }
0719: return result;
0720: }
0721:
0722: if (!typeFrame.isValid()) {
0723: return new HashSet<JavaClassAndMethod>();
0724: }
0725:
0726: Type receiverType;
0727: boolean receiverTypeIsExact;
0728:
0729: if (opcode == Constants.INVOKESPECIAL) {
0730: // invokespecial instructions are dispatched to EXACTLY
0731: // the class specified by the instruction
0732: receiverType = ObjectTypeFactory
0733: .getInstance(invokeInstruction.getClassName(cpg));
0734: receiverTypeIsExact = false; // Doesn't actually matter
0735: } else {
0736: // For invokevirtual and invokeinterface instructions, we have
0737: // virtual dispatch. By taking the receiver type (which may be a
0738: // subtype of the class specified by the instruction),
0739: // we may get a more precise set of call targets.
0740: int instanceStackLocation = typeFrame
0741: .getInstanceStackLocation(invokeInstruction, cpg);
0742: receiverType = typeFrame
0743: .getStackValue(instanceStackLocation);
0744: if (!(receiverType instanceof ReferenceType)) {
0745: return new HashSet<JavaClassAndMethod>();
0746: }
0747: receiverTypeIsExact = typeFrame
0748: .isExact(instanceStackLocation);
0749: }
0750: if (DEBUG_METHOD_LOOKUP) {
0751: System.out.println("[receiver type is " + receiverType
0752: + ", "
0753: + (receiverTypeIsExact ? "exact]" : " not exact]"));
0754: }
0755:
0756: return resolveMethodCallTargets((ReferenceType) receiverType,
0757: invokeInstruction, cpg, receiverTypeIsExact);
0758: }
0759:
0760: /**
0761: * Resolve possible instance method call targets.
0762: * Assumes that invokevirtual and invokeinterface methods may
0763: * call any subtype of the receiver class.
0764: *
0765: * @param receiverType type of the receiver object
0766: * @param invokeInstruction the InvokeInstruction
0767: * @param cpg the ConstantPoolGen
0768: * @return Set of methods which might be called
0769: * @throws ClassNotFoundException
0770: */
0771: public static Set<JavaClassAndMethod> resolveMethodCallTargets(
0772: ReferenceType receiverType,
0773: InvokeInstruction invokeInstruction, ConstantPoolGen cpg)
0774: throws ClassNotFoundException {
0775: return resolveMethodCallTargets(receiverType,
0776: invokeInstruction, cpg, false);
0777: }
0778:
0779: /**
0780: * Resolve possible instance method call targets.
0781: *
0782: * @param receiverType type of the receiver object
0783: * @param invokeInstruction the InvokeInstruction
0784: * @param cpg the ConstantPoolGen
0785: * @param receiverTypeIsExact if true, the receiver type is known exactly,
0786: * which should allow a precise result
0787: * @return Set of methods which might be called
0788: * @throws ClassNotFoundException
0789: */
0790: public static Set<JavaClassAndMethod> resolveMethodCallTargets(
0791: ReferenceType receiverType,
0792: InvokeInstruction invokeInstruction, ConstantPoolGen cpg,
0793: boolean receiverTypeIsExact) throws ClassNotFoundException {
0794: HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>();
0795:
0796: if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC)
0797: throw new IllegalArgumentException();
0798:
0799: String methodName = invokeInstruction.getName(cpg);
0800: String methodSig = invokeInstruction.getSignature(cpg);
0801:
0802: // Array method calls aren't virtual.
0803: // They should just resolve to Object methods.
0804: if (receiverType instanceof ArrayType) {
0805: JavaClass javaLangObject = AnalysisContext
0806: .currentAnalysisContext().lookupClass(
0807: "java.lang.Object");
0808: JavaClassAndMethod classAndMethod = findMethod(
0809: javaLangObject, methodName, methodSig,
0810: INSTANCE_METHOD);
0811: if (classAndMethod != null)
0812: result.add(classAndMethod);
0813: return result;
0814: }
0815:
0816: AnalysisContext analysisContext = AnalysisContext
0817: .currentAnalysisContext();
0818:
0819: // Get the receiver class.
0820: String receiverClassName = ((ObjectType) receiverType)
0821: .getClassName();
0822: JavaClass receiverClass = analysisContext
0823: .lookupClass(receiverClassName);
0824: ClassDescriptor receiverDesc = DescriptorFactory
0825: .createClassDescriptorFromDottedClassName(receiverClassName);
0826:
0827: // Figure out the upper bound for the method.
0828: // This is what will be called if this is not a virtual call site.
0829: JavaClassAndMethod upperBound = findMethod(receiverClass,
0830: methodName, methodSig, CONCRETE_METHOD);
0831: if (upperBound == null) {
0832: upperBound = findInvocationLeastUpperBound(receiverClass,
0833: methodName, methodSig, CONCRETE_METHOD, false);
0834: }
0835: if (upperBound != null) {
0836: if (DEBUG_METHOD_LOOKUP) {
0837: System.out.println("Adding upper bound: "
0838: + SignatureConverter.convertMethodSignature(
0839: upperBound.getJavaClass(), upperBound
0840: .getMethod()));
0841: }
0842: result.add(upperBound);
0843: }
0844:
0845: // Is this a virtual call site?
0846: boolean virtualCall = (invokeInstruction.getOpcode() == Constants.INVOKEVIRTUAL || invokeInstruction
0847: .getOpcode() == Constants.INVOKEINTERFACE)
0848: && (upperBound == null || !upperBound.getJavaClass()
0849: .isFinal()
0850: && !upperBound.getMethod().isFinal())
0851: && !receiverTypeIsExact;
0852:
0853: if (virtualCall) {
0854: if (!receiverClassName.equals("java.lang.Object")) {
0855:
0856: // This is a true virtual call: assume that any concrete
0857: // subtype method may be called.
0858: Set<ClassDescriptor> subTypeSet = analysisContext
0859: .getSubtypes2().getSubtypes(receiverDesc);
0860: for (ClassDescriptor subtype : subTypeSet) {
0861: XMethod concreteSubtypeMethod = findMethod(subtype,
0862: methodName, methodSig, false);
0863: if (concreteSubtypeMethod != null
0864: && (concreteSubtypeMethod.getAccessFlags() & Constants.ACC_ABSTRACT) == 0) {
0865: result.add(new JavaClassAndMethod(
0866: concreteSubtypeMethod));
0867: }
0868: }
0869: if (false && subTypeSet.size() > 500)
0870: new RuntimeException(receiverClassName + " has "
0871: + subTypeSet.size() + " subclasses, "
0872: + result.size() + " of which implement "
0873: + methodName + methodSig + " "
0874: + invokeInstruction)
0875: .printStackTrace(System.out);
0876:
0877: }
0878: }
0879: return result;
0880: }
0881:
0882: /**
0883: * Return whether or not the given method is concrete.
0884: *
0885: * @param xmethod the method
0886: * @return true if the method is concrete, false otherwise
0887: */
0888: @Deprecated
0889: public static boolean isConcrete(XMethod xmethod) {
0890: int accessFlags = xmethod.getAccessFlags();
0891: return (accessFlags & Constants.ACC_ABSTRACT) == 0
0892: && (accessFlags & Constants.ACC_NATIVE) == 0;
0893: }
0894:
0895: /**
0896: * Find a field with given name defined in given class.
0897: *
0898: * @param className the name of the class
0899: * @param fieldName the name of the field
0900: * @return the Field, or null if no such field could be found
0901: */
0902: public static Field findField(String className, String fieldName)
0903: throws ClassNotFoundException {
0904: JavaClass jclass = Repository.lookupClass(className);
0905:
0906: while (jclass != null) {
0907: Field[] fieldList = jclass.getFields();
0908: for (Field field : fieldList) {
0909: if (field.getName().equals(fieldName)) {
0910: return field;
0911: }
0912: }
0913:
0914: jclass = jclass.getSuperClass();
0915: }
0916:
0917: return null;
0918: }
0919:
0920: /**
0921: * Look up a field with given name and signature in given class,
0922: * returning it as an {@link XField XField} object.
0923: * If a field can't be found in the immediate class,
0924: * its superclass is search, and so forth.
0925: *
0926: * @param className name of the class through which the field
0927: * is referenced
0928: * @param fieldName name of the field
0929: * @param fieldSig signature of the field
0930: * @param isStatic true if field is static, false otherwise
0931: * @return an XField object representing the field, or null if no such field could be found
0932: */
0933: public static XField findXField(String className, String fieldName,
0934: String fieldSig, boolean isStatic)
0935: throws ClassNotFoundException {
0936:
0937: return XFactory.createXField(className, fieldName, fieldSig,
0938: isStatic);
0939: }
0940:
0941: /**
0942: * Look up the field referenced by given FieldInstruction,
0943: * returning it as an {@link XField XField} object.
0944: *
0945: * @param fins the FieldInstruction
0946: * @param cpg the ConstantPoolGen used by the class containing the instruction
0947: * @return an XField object representing the field, or null
0948: * if no such field could be found
0949: */
0950: public static XField findXField(FieldInstruction fins, @NonNull
0951: ConstantPoolGen cpg) throws ClassNotFoundException {
0952:
0953: String className = fins.getClassName(cpg);
0954: String fieldName = fins.getFieldName(cpg);
0955: String fieldSig = fins.getSignature(cpg);
0956:
0957: boolean isStatic = (fins.getOpcode() == Constants.GETSTATIC || fins
0958: .getOpcode() == Constants.PUTSTATIC);
0959:
0960: XField xfield = findXField(className, fieldName, fieldSig,
0961: isStatic);
0962: short opcode = fins.getOpcode();
0963: if (xfield != null
0964: && xfield.isStatic() == (opcode == Constants.GETSTATIC || opcode == Constants.PUTSTATIC))
0965: return xfield;
0966: else
0967: return null;
0968: }
0969:
0970: /**
0971: * Determine whether the given INVOKESTATIC instruction
0972: * is an inner-class field accessor method.
0973: * @param inv the INVOKESTATIC instruction
0974: * @param cpg the ConstantPoolGen for the method
0975: * @return true if the instruction is an inner-class field accessor, false if not
0976: */
0977: public static boolean isInnerClassAccess(INVOKESTATIC inv,
0978: ConstantPoolGen cpg) {
0979: String methodName = inv.getName(cpg);
0980: return methodName.startsWith("access$");
0981: }
0982:
0983: /**
0984: * Get the InnerClassAccess for access method called
0985: * by given INVOKESTATIC.
0986: * @param inv the INVOKESTATIC instruction
0987: * @param cpg the ConstantPoolGen for the method
0988: * @return the InnerClassAccess, or null if the instruction is not
0989: * an inner-class access
0990: */
0991: public static InnerClassAccess getInnerClassAccess(
0992: INVOKESTATIC inv, ConstantPoolGen cpg)
0993: throws ClassNotFoundException {
0994:
0995: String className = inv.getClassName(cpg);
0996: String methodName = inv.getName(cpg);
0997: String methodSig = inv.getSignature(cpg);
0998:
0999: InnerClassAccess access = AnalysisContext
1000: .currentAnalysisContext().getInnerClassAccessMap()
1001: .getInnerClassAccess(className, methodName);
1002: return (access != null && access.getMethodSignature().equals(
1003: methodSig)) ? access : null;
1004: }
1005:
1006: }
1007:
1008: // vim:ts=4
|