All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.ba.Hierarchy Maven / Gradle / Ivy

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

package edu.umd.cs.findbugs.ba;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import org.apache.bcel.Const;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.type.NullType;
import edu.umd.cs.findbugs.ba.type.TypeFrame;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.util.Values;

/**
 * Facade for class hierarchy queries. These typically access the class
 * hierarchy using the {@link org.apache.bcel.Repository} class. Callers should
 * generally expect to handle ClassNotFoundException for when referenced classes
 * can't be found.
 *
 * @author David Hovemeyer
 */
public class Hierarchy {
    protected static final boolean DEBUG_METHOD_LOOKUP = SystemProperties.getBoolean("hier.lookup.debug");

    public static final ClassDescriptor RUNTIME_EXCEPTION = DescriptorFactory.createClassDescriptor(RuntimeException.class);
    public static final ClassDescriptor EXCEPTION = DescriptorFactory.createClassDescriptor(Exception.class);
    public static final ClassDescriptor ERROR = DescriptorFactory.createClassDescriptor(Error.class);

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

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

    /**
     * Type of java.lang.RuntimeException.
     */
    public static final ObjectType RUNTIME_EXCEPTION_TYPE = ObjectTypeFactory.getInstance("java.lang.RuntimeException");

    /**
     * Determine whether one class (or reference type) is a subtype of another.
     *
     * @param clsName
     *            the name of the class or reference type
     * @param possibleSupertypeClassName
     *            the name of the possible superclass
     * @return true if clsName is a subtype of possibleSupertypeClassName, false
     *         if not
     */
    public static boolean isSubtype(@DottedClassName String clsName, @DottedClassName String possibleSupertypeClassName)
            throws ClassNotFoundException {
        Subtypes2 subtypes2 = Global.getAnalysisCache().getDatabase(Subtypes2.class);
        return subtypes2.isSubtype(DescriptorFactory.createClassDescriptorFromDottedClassName(clsName), DescriptorFactory
                .createClassDescriptorFromDottedClassName(possibleSupertypeClassName));
    }

    /**
     * Determine if one reference type is a subtype of another.
     *
     * @param t
     *            a reference type
     * @param possibleSupertype
     *            the possible supertype
     * @return true if t is a subtype of possibleSupertype, false if not
     */
    public static boolean isSubtype(ReferenceType t, ReferenceType possibleSupertype) throws ClassNotFoundException {
        return Global.getAnalysisCache().getDatabase(Subtypes2.class).isSubtype(t, possibleSupertype);
    }


    /**
     * Determine if the given ObjectType reference represents a
     * universal exception handler. That is, one that will catch any
     * kind of exception.
     *
     * @param catchType
     *            the ObjectType of the exception handler
     * @return true if catchType is null, or if catchType is java.lang.Throwable
     */
    public static boolean isUniversalExceptionHandler(ObjectType catchType) {
        return catchType == null || catchType.equals(Type.THROWABLE);
    }

    /**
     * Determine if the given ObjectType refers to an unchecked exception
     * (RuntimeException or Error).
     */
    public static boolean isUncheckedException(ObjectType type) throws ClassNotFoundException {
        if (type.equals(Type.THROWABLE) || type.equals(RUNTIME_EXCEPTION_TYPE) || type.equals(ERROR_TYPE)) {
            return true;
        }
        ClassDescriptor c = DescriptorFactory.getClassDescriptor(type);
        Subtypes2 subtypes2 = Global.getAnalysisCache().getDatabase(Subtypes2.class);
        return subtypes2.isSubtype(c, RUNTIME_EXCEPTION, ERROR);

    }

    /**
     * Determine if method whose name and signature is specified is a monitor
     * wait operation.
     *
     * @param methodName
     *            name of the method
     * @param methodSig
     *            signature of the method
     * @return true if the method is a monitor wait, false if not
     */
    public static boolean isMonitorWait(String methodName, String methodSig) {
        return "wait".equals(methodName) && ("()V".equals(methodSig) || "(J)V".equals(methodSig) || "(JI)V".equals(methodSig));
    }

    /**
     * Determine if given Instruction is a monitor wait.
     *
     * @param ins
     *            the Instruction
     * @param cpg
     *            the ConstantPoolGen for the Instruction
     *
     * @return true if the instruction is a monitor wait, false if not
     */
    public static boolean isMonitorWait(Instruction ins, ConstantPoolGen cpg) {
        if (!(ins instanceof InvokeInstruction)) {
            return false;
        }
        if (ins.getOpcode() == Const.INVOKESTATIC) {
            return false;
        }

        InvokeInstruction inv = (InvokeInstruction) ins;
        String methodName = inv.getMethodName(cpg);
        String methodSig = inv.getSignature(cpg);

        return isMonitorWait(methodName, methodSig);
    }

    /**
     * Determine if method whose name and signature is specified is a monitor
     * notify operation.
     *
     * @param methodName
     *            name of the method
     * @param methodSig
     *            signature of the method
     * @return true if the method is a monitor notify, false if not
     */
    public static boolean isMonitorNotify(String methodName, String methodSig) {
        return ("notify".equals(methodName) || "notifyAll".equals(methodName)) && "()V".equals(methodSig);
    }

    /**
     * Determine if given Instruction is a monitor wait.
     *
     * @param ins
     *            the Instruction
     * @param cpg
     *            the ConstantPoolGen for the Instruction
     *
     * @return true if the instruction is a monitor wait, false if not
     */
    public static boolean isMonitorNotify(Instruction ins, ConstantPoolGen cpg) {
        if (!(ins instanceof InvokeInstruction)) {
            return false;
        }
        if (ins.getOpcode() == Const.INVOKESTATIC) {
            return false;
        }

        InvokeInstruction inv = (InvokeInstruction) ins;
        String methodName = inv.getMethodName(cpg);
        String methodSig = inv.getSignature(cpg);

        return isMonitorNotify(methodName, methodSig);
    }

    /**
     * Look up the method referenced by given InvokeInstruction. This method
     * does not look for implementations in super or subclasses
     * according to the virtual dispatch rules.
     *
     * @param inv
     *            the InvokeInstruction
     * @param cpg
     *            the ConstantPoolGen used by the class the InvokeInstruction
     *            belongs to
     * @return the JavaClassAndMethod, or null if no such method is defined in
     *         the class
     */
    public static JavaClassAndMethod findExactMethod(InvokeInstruction inv, ConstantPoolGen cpg) throws ClassNotFoundException {
        return findExactMethod(inv, cpg, ANY_METHOD);
    }

    /**
     * Look up the method referenced by given InvokeInstruction. This method
     * does not look for implementations in super or subclasses
     * according to the virtual dispatch rules.
     *
     * @param inv
     *            the InvokeInstruction
     * @param cpg
     *            the ConstantPoolGen used by the class the InvokeInstruction
     *            belongs to
     * @param chooser
     *            JavaClassAndMethodChooser to use to pick the method from among
     *            the candidates
     * @return the JavaClassAndMethod, or null if no such method is defined in
     *         the class
     */
    public static JavaClassAndMethod findExactMethod(InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser chooser)
            throws ClassNotFoundException {
        String className = inv.getClassName(cpg);
        String methodName = inv.getName(cpg);
        String methodSig = inv.getSignature(cpg);

        JavaClass jclass = Repository.lookupClass(className);
        return findMethod(jclass, methodName, methodSig, chooser);
    }

    /**
     * Visit all superclass methods which the given method overrides.
     *
     * @param method
     *            the method
     * @param chooser
     *            chooser which visits each superclass method
     * @return the chosen method, or null if no method is chosen
     * @throws ClassNotFoundException
     */
    public static JavaClassAndMethod visitSuperClassMethods(JavaClassAndMethod method, JavaClassAndMethodChooser chooser)
            throws ClassNotFoundException {
        return findMethod(method.getJavaClass().getSuperClasses(), method.getMethod().getName(), method.getMethod()
                .getSignature(), chooser);
    }

    /**
     * Visit all superinterface methods which the given method implements.
     *
     * @param method
     *            the method
     * @param chooser
     *            chooser which visits each superinterface method
     * @return the chosen method, or null if no method is chosen
     * @throws ClassNotFoundException
     */
    public static JavaClassAndMethod visitSuperInterfaceMethods(JavaClassAndMethod method, JavaClassAndMethodChooser chooser)
            throws ClassNotFoundException {
        return findMethod(method.getJavaClass().getAllInterfaces(), method.getMethod().getName(), method.getMethod()
                .getSignature(), chooser);
    }

    /**
     * 

Find the least upper bound method in the class hierarchy which could be * called by the given InvokeInstruction. One reason this method is useful * is that it indicates which declared exceptions are thrown by the called * methods.

*
    *
  • For invokespecial, this is simply an exact lookup.
  • *
  • For invokestatic and invokevirtual, the named class is searched, * followed by superclasses up to the root of the object hierarchy * (java.lang.Object). Yes, invokestatic really is declared to check * superclasses. See VMSpec, 2nd ed, sec. 5.4.3.3.
  • *
  • For invokeinterface, the named class is searched, followed by all * interfaces transitively declared by the class. (Question: is the order * important here? Maybe the VM spec requires that the actual interface * desired is given, so the extended lookup will not be required. Should * check.)
  • *
* * @param inv * the InvokeInstruction * @param cpg * the ConstantPoolGen used by the class the InvokeInstruction * belongs to * @return the JavaClassAndMethod, or null if no matching method can be * found */ public static @CheckForNull JavaClassAndMethod findInvocationLeastUpperBound(InvokeInstruction inv, ConstantPoolGen cpg) throws ClassNotFoundException { return findInvocationLeastUpperBound(inv, cpg, ANY_METHOD); } public static @CheckForNull JavaClassAndMethod findInvocationLeastUpperBound(InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser methodChooser) throws ClassNotFoundException { if (DEBUG_METHOD_LOOKUP) { System.out.println("Find prototype method for " + SignatureConverter.convertMethodSignature(inv, cpg)); } short opcode = inv.getOpcode(); if (opcode == Const.INVOKESTATIC) { if (methodChooser == INSTANCE_METHOD) { return null; } } else { if (methodChooser == STATIC_METHOD) { return null; } } // Find the method if (opcode == Const.INVOKESPECIAL) { // Non-virtual dispatch return findExactMethod(inv, cpg, methodChooser); } if (opcode == Const.INVOKEDYNAMIC) { return null; } String className = inv.getClassName(cpg); String methodName = inv.getName(cpg); String methodSig = inv.getSignature(cpg); if (DEBUG_METHOD_LOOKUP) { System.out.println("[Class name is " + className + "]"); System.out.println("[Method name is " + methodName + "]"); System.out.println("[Method signature is " + methodSig + "]"); } if (className.startsWith(Values.SIG_ARRAY_PREFIX)) { // Java 1.5 allows array classes to appear as the class name className = Values.DOTTED_JAVA_LANG_OBJECT; } JavaClass jClass = Repository.lookupClass(className); return findInvocationLeastUpperBound(jClass, methodName, methodSig, methodChooser, opcode == Const.INVOKEINTERFACE); } public static @CheckForNull JavaClassAndMethod findInvocationLeastUpperBound(JavaClass jClass, String methodName, String methodSig, JavaClassAndMethodChooser methodChooser, boolean invokeInterface) throws ClassNotFoundException { JavaClassAndMethod result = findMethod(jClass, methodName, methodSig, methodChooser); if (result != null) { return result; } if (invokeInterface) { for (JavaClass i : jClass.getInterfaces()) { result = findInvocationLeastUpperBound(i, methodName, methodSig, methodChooser, invokeInterface); if (result != null) { return null; } } } else { JavaClass sClass = jClass.getSuperClass(); if (sClass != null) { return findInvocationLeastUpperBound(sClass, methodName, methodSig, methodChooser, invokeInterface); } } return null; } /** * Find the declared exceptions for the method called by given instruction. * * @param inv * the InvokeInstruction * @param cpg * the ConstantPoolGen used by the class the InvokeInstruction * belongs to * @return array of ObjectTypes of thrown exceptions, or null if we can't * find the list of declared exceptions * @deprecated Use * {@link Hierarchy2#findDeclaredExceptions(InvokeInstruction,ConstantPoolGen)} * instead */ @Deprecated public static ObjectType[] findDeclaredExceptions(InvokeInstruction inv, ConstantPoolGen cpg) { return Hierarchy2.findDeclaredExceptions(inv, cpg); } /** * Find a method in given class. * * @param javaClass * the class * @param methodName * the name of the method * @param methodSig * the signature of the method * @return the JavaClassAndMethod, or null if no such method exists in the * class */ public static @CheckForNull JavaClassAndMethod findMethod(JavaClass javaClass, String methodName, String methodSig) { return findMethod(javaClass, methodName, methodSig, ANY_METHOD); } public static @CheckForNull JavaClassAndMethod findMethod(JavaClass javaClass, String methodName, String methodSig, JavaClassAndMethodChooser chooser) { if (DEBUG_METHOD_LOOKUP) { System.out.println("Check " + javaClass.getClassName()); } Method[] methodList = javaClass.getMethods(); for (Method method : methodList) { if (method.getName().equals(methodName) && method.getSignature().equals(methodSig)) { JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method); if (chooser.choose(m)) { if (DEBUG_METHOD_LOOKUP) { System.out.println("\t==> FOUND: " + method); } return m; } } } if (DEBUG_METHOD_LOOKUP) { System.out.println("\t==> NOT FOUND"); } return null; } /** * Find a method in given class. * * @param classDesc * the class descriptor * @param methodName * the name of the method * @param methodSig * the signature of the method * @param isStatic * are we looking for a static method? * @return the JavaClassAndMethod, or null if no such method exists in the * class */ public static @CheckForNull XMethod findMethod(ClassDescriptor classDesc, String methodName, String methodSig, boolean isStatic) { if (DEBUG_METHOD_LOOKUP) { System.out.println("Check " + classDesc.getClassName()); } try { XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc); return xClass.findMethod(methodName, methodSig, isStatic); } catch (CheckedAnalysisException e) { AnalysisContext.logError("Error looking for " + classDesc + "." + methodName + methodSig, e); return null; } } /** * Find a method in given class. * * @param javaClass * the class * @param methodName * the name of the method * @param methodSig * the signature of the method * @return the JavaClassAndMethod, or null if no such method exists in the * class */ @Deprecated public static @CheckForNull JavaClassAndMethod findConcreteMethod(JavaClass javaClass, String methodName, String methodSig) { if (DEBUG_METHOD_LOOKUP) { System.out.println("Check " + javaClass.getClassName()); } Method[] methodList = javaClass.getMethods(); for (Method method : methodList) { if (method.getName().equals(methodName) && method.getSignature().equals(methodSig) && accessFlagsAreConcrete(method.getAccessFlags())) { JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method); return m; } } if (DEBUG_METHOD_LOOKUP) { System.out.println("\t==> NOT FOUND"); } return null; } /** * Find a method in given class. * * @param javaClass * the class * @param methodName * the name of the method * @param methodSig * the signature of the method * @param chooser * the JavaClassAndMethodChooser to use to screen possible * candidates * @return the XMethod, or null if no such method exists in the class */ @Deprecated public static @CheckForNull XMethod findXMethod(JavaClass javaClass, String methodName, String methodSig, JavaClassAndMethodChooser chooser) { JavaClassAndMethod result = findMethod(javaClass, methodName, methodSig, chooser); return result == null ? null : XFactory.createXMethod(result.getJavaClass(), result.getMethod()); } /** * JavaClassAndMethodChooser which accepts any method. */ public static final JavaClassAndMethodChooser ANY_METHOD = new JavaClassAndMethodChooser() { @Override public boolean choose(JavaClassAndMethod javaClassAndMethod) { return true; } @Override public boolean choose(XMethod method) { return true; } }; // FIXME: perhaps native methods should be concrete. public static boolean accessFlagsAreConcrete(int accessFlags) { return (accessFlags & Const.ACC_ABSTRACT) == 0 && (accessFlags & Const.ACC_NATIVE) == 0; } /** * JavaClassAndMethodChooser which accepts only concrete (not abstract or * native) methods. */ public static final JavaClassAndMethodChooser CONCRETE_METHOD = new JavaClassAndMethodChooser() { @Override public boolean choose(JavaClassAndMethod javaClassAndMethod) { Method method = javaClassAndMethod.getMethod(); int accessFlags = method.getAccessFlags(); return accessFlagsAreConcrete(accessFlags); } @Override public boolean choose(XMethod method) { return accessFlagsAreConcrete(method.getAccessFlags()); } }; /** * JavaClassAndMethodChooser which accepts only static methods. */ public static final JavaClassAndMethodChooser STATIC_METHOD = new JavaClassAndMethodChooser() { @Override public boolean choose(JavaClassAndMethod javaClassAndMethod) { return javaClassAndMethod.getMethod().isStatic(); } @Override public boolean choose(XMethod method) { return method.isStatic(); } }; /** * JavaClassAndMethodChooser which accepts only instance methods. */ public static final JavaClassAndMethodChooser INSTANCE_METHOD = new JavaClassAndMethodChooser() { @Override public boolean choose(JavaClassAndMethod javaClassAndMethod) { return !javaClassAndMethod.getMethod().isStatic(); } @Override public boolean choose(XMethod method) { return !method.isStatic(); } }; /** * Find a method in given list of classes, searching the classes in order. * * @param classList * list of classes in which to search * @param methodName * the name of the method * @param methodSig * the signature of the method * @return the JavaClassAndMethod, or null if no such method exists in the * class */ @Deprecated public static JavaClassAndMethod findMethod(JavaClass[] classList, String methodName, String methodSig) { return findMethod(classList, methodName, methodSig, ANY_METHOD); } /** * Find a method in given list of classes, searching the classes in order. * * @param classList * list of classes in which to search * @param methodName * the name of the method * @param methodSig * the signature of the method * @param chooser * JavaClassAndMethodChooser to select which methods are * considered; it must return true for a method to be returned * @return the JavaClassAndMethod, or null if no such method exists in the * class */ public static JavaClassAndMethod findMethod(JavaClass[] classList, String methodName, String methodSig, JavaClassAndMethodChooser chooser) { JavaClassAndMethod m = null; for (JavaClass cls : classList) { if ((m = findMethod(cls, methodName, methodSig, chooser)) != null) { break; } } return m; } /** * Find XMethod for method in given list of classes, searching the classes * in order. * * @param classList * list of classes in which to search * @param methodName * the name of the method * @param methodSig * the signature of the method * @return the XMethod, or null if no such method exists in the class */ @Deprecated public static XMethod findXMethod(JavaClass[] classList, String methodName, String methodSig) { return findXMethod(classList, methodName, methodSig, ANY_METHOD); } /** * Find XMethod for method in given list of classes, searching the classes * in order. * * @param classList * list of classes in which to search * @param methodName * the name of the method * @param methodSig * the signature of the method * @param chooser * JavaClassAndMethodChooser to select which methods are * considered; it must return true for a method to be returned * @return the XMethod, or null if no such method exists in the class */ @Deprecated public static XMethod findXMethod(JavaClass[] classList, String methodName, String methodSig, JavaClassAndMethodChooser chooser) { for (JavaClass cls : classList) { JavaClassAndMethod m; if ((m = findMethod(cls, methodName, methodSig)) != null && chooser.choose(m)) { return XFactory.createXMethod(cls, m.getMethod()); } } return null; } /** * Resolve possible method call targets. This works for both static and * instance method calls. * * @param invokeInstruction * the InvokeInstruction * @param typeFrame * the TypeFrame containing the types of stack values * @param cpg * the ConstantPoolGen * @return Set of methods which might be called * @throws DataflowAnalysisException * @throws ClassNotFoundException */ public static Set resolveMethodCallTargets(InvokeInstruction invokeInstruction, TypeFrame typeFrame, ConstantPoolGen cpg) throws DataflowAnalysisException, ClassNotFoundException { short opcode = invokeInstruction.getOpcode(); if (opcode == Const.INVOKESTATIC) { HashSet result = new HashSet<>(); JavaClassAndMethod targetMethod = findInvocationLeastUpperBound(invokeInstruction, cpg, CONCRETE_METHOD); if (targetMethod != null) { result.add(targetMethod); } return result; } if (!typeFrame.isValid()) { return new HashSet<>(); } Type receiverType; boolean receiverTypeIsExact; if (opcode == Const.INVOKESPECIAL) { // invokespecial instructions are dispatched to EXACTLY // the class specified by the instruction receiverType = ObjectTypeFactory.getInstance(invokeInstruction.getClassName(cpg)); receiverTypeIsExact = false; // Doesn't actually matter } else if (opcode == Const.INVOKEDYNAMIC) { // XXX handle INVOKEDYNAMIC return new HashSet<>(); } else { // For invokevirtual and invokeinterface instructions, we have // virtual dispatch. By taking the receiver type (which may be a // subtype of the class specified by the instruction), // we may get a more precise set of call targets. int instanceStackLocation = typeFrame.getInstanceStackLocation(invokeInstruction, cpg); receiverType = typeFrame.getStackValue(instanceStackLocation); if (!(receiverType instanceof ReferenceType)) { return new HashSet<>(); } receiverTypeIsExact = typeFrame.isExact(instanceStackLocation); } if (DEBUG_METHOD_LOOKUP) { System.out.println("[receiver type is " + receiverType + ", " + (receiverTypeIsExact ? "exact]" : " not exact]")); } return resolveMethodCallTargets((ReferenceType) receiverType, invokeInstruction, cpg, receiverTypeIsExact); } /** * Resolve possible instance method call targets. Assumes that invokevirtual * and invokeinterface methods may call any subtype of the receiver class. * * @param receiverType * type of the receiver object * @param invokeInstruction * the InvokeInstruction * @param cpg * the ConstantPoolGen * @return Set of methods which might be called * @throws ClassNotFoundException */ public static Set resolveMethodCallTargets(ReferenceType receiverType, InvokeInstruction invokeInstruction, ConstantPoolGen cpg) throws ClassNotFoundException { return resolveMethodCallTargets(receiverType, invokeInstruction, cpg, false); } /** * Resolve possible instance method call targets. * * @param receiverType * type of the receiver object * @param invokeInstruction * the InvokeInstruction * @param cpg * the ConstantPoolGen * @param receiverTypeIsExact * if true, the receiver type is known exactly, which should * allow a precise result * @return Set of methods which might be called * @throws ClassNotFoundException */ public static Set resolveMethodCallTargets(ReferenceType receiverType, InvokeInstruction invokeInstruction, ConstantPoolGen cpg, boolean receiverTypeIsExact) throws ClassNotFoundException { HashSet result = new HashSet<>(); if (invokeInstruction.getOpcode() == Const.INVOKESTATIC) { throw new IllegalArgumentException(); } String methodName = invokeInstruction.getName(cpg); String methodSig = invokeInstruction.getSignature(cpg); // Array method calls aren't virtual. // They should just resolve to Object methods. if (receiverType instanceof ArrayType) { JavaClass javaLangObject = AnalysisContext.currentAnalysisContext().lookupClass(Values.DOTTED_JAVA_LANG_OBJECT); JavaClassAndMethod classAndMethod = findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD); if (classAndMethod != null) { result.add(classAndMethod); } return result; } if (receiverType instanceof NullType) { return Collections.emptySet(); } AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext(); // Get the receiver class. String receiverClassName = ((ObjectType) receiverType).getClassName(); JavaClass receiverClass = analysisContext.lookupClass(receiverClassName); ClassDescriptor receiverDesc = DescriptorFactory.createClassDescriptorFromDottedClassName(receiverClassName); // Figure out the upper bound for the method. // This is what will be called if this is not a virtual call site. JavaClassAndMethod upperBound = findMethod(receiverClass, methodName, methodSig, CONCRETE_METHOD); if (upperBound == null) { upperBound = findInvocationLeastUpperBound(receiverClass, methodName, methodSig, CONCRETE_METHOD, false); } if (upperBound != null) { if (DEBUG_METHOD_LOOKUP) { System.out.println("Adding upper bound: " + SignatureConverter.convertMethodSignature(upperBound.getJavaClass(), upperBound.getMethod())); } result.add(upperBound); } // Is this a virtual call site? boolean virtualCall = (invokeInstruction.getOpcode() == Const.INVOKEVIRTUAL || invokeInstruction.getOpcode() == Const.INVOKEINTERFACE) && (upperBound == null || !upperBound.getJavaClass().isFinal() && !upperBound.getMethod().isFinal()) && !receiverTypeIsExact; if (virtualCall) { if (!Values.DOTTED_JAVA_LANG_OBJECT.equals(receiverClassName)) { // This is a true virtual call: assume that any concrete // subtype method may be called. Set subTypeSet = analysisContext.getSubtypes2().getSubtypes(receiverDesc); for (ClassDescriptor subtype : subTypeSet) { XMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, false); if (concreteSubtypeMethod != null && (concreteSubtypeMethod.getAccessFlags() & Const.ACC_ABSTRACT) == 0) { result.add(new JavaClassAndMethod(concreteSubtypeMethod)); } } if (false && subTypeSet.size() > 500) { new RuntimeException(receiverClassName + " has " + subTypeSet.size() + " subclasses, " + result.size() + " of which implement " + methodName + methodSig + " " + invokeInstruction) .printStackTrace(System.out); } } } return result; } /** * Return whether or not the given method is concrete. * * @param xmethod * the method * @return true if the method is concrete, false otherwise */ @Deprecated public static boolean isConcrete(XMethod xmethod) { int accessFlags = xmethod.getAccessFlags(); return (accessFlags & Const.ACC_ABSTRACT) == 0 && (accessFlags & Const.ACC_NATIVE) == 0; } /** * Find a field with given name defined in given class. * * @param className * the name of the class * @param fieldName * the name of the field * @return the Field, or null if no such field could be found */ public static Field findField(String className, String fieldName) throws ClassNotFoundException { JavaClass jclass = Repository.lookupClass(className); while (jclass != null) { Field[] fieldList = jclass.getFields(); for (Field field : fieldList) { if (field.getName().equals(fieldName)) { return field; } } jclass = jclass.getSuperClass(); } return null; } /** * Look up a field with given name and signature in given class, returning * it as an {@link XField XField} object. If a field can't be found in the * immediate class, its superclass is search, and so forth. * * @param className * name of the class through which the field is referenced * @param fieldName * name of the field * @param fieldSig * signature of the field * @param isStatic * true if field is static, false otherwise * @return an XField object representing the field, or null if no such field * could be found */ public static XField findXField(String className, String fieldName, String fieldSig, boolean isStatic) { return XFactory.createXField(className, fieldName, fieldSig, isStatic); } /** * Look up the field referenced by given FieldInstruction, returning it as * an {@link XField XField} object. * * @param fins * the FieldInstruction * @param cpg * the ConstantPoolGen used by the class containing the * instruction * @return an XField object representing the field, or null if no such field * could be found */ public static @CheckForNull XField findXField(FieldInstruction fins, @Nonnull ConstantPoolGen cpg) { String className = fins.getClassName(cpg); String fieldName = fins.getFieldName(cpg); String fieldSig = fins.getSignature(cpg); boolean isStatic = (fins.getOpcode() == Const.GETSTATIC || fins.getOpcode() == Const.PUTSTATIC); XField xfield = findXField(className, fieldName, fieldSig, isStatic); short opcode = fins.getOpcode(); if (xfield != null && xfield.isResolved() && xfield.isStatic() == (opcode == Const.GETSTATIC || opcode == Const.PUTSTATIC)) { return xfield; } else { return null; } } /** * Determine whether the given INVOKESTATIC instruction is an inner-class * field accessor method. * * @param inv * the INVOKESTATIC instruction * @param cpg * the ConstantPoolGen for the method * @return true if the instruction is an inner-class field accessor, false * if not */ public static boolean isInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) { String methodName = inv.getName(cpg); return methodName.startsWith("access$"); } /** * Get the InnerClassAccess for access method called by given INVOKESTATIC. * * @param inv * the INVOKESTATIC instruction * @param cpg * the ConstantPoolGen for the method * @return the InnerClassAccess, or null if the instruction is not an * inner-class access */ public static InnerClassAccess getInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) throws ClassNotFoundException { String className = inv.getClassName(cpg); String methodName = inv.getName(cpg); String methodSig = inv.getSignature(cpg); InnerClassAccess access = AnalysisContext.currentAnalysisContext().getInnerClassAccessMap() .getInnerClassAccess(className, methodName); return (access != null && access.getMethodSignature().equals(methodSig)) ? access : null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy