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 == Constants.INVOKESTATIC) { if (methodChooser == INSTANCE_METHOD) return null; } else { if (methodChooser == STATIC_METHOD) return null; } // Find the method if (opcode == Constants.INVOKESPECIAL) { // Non-virtual dispatch return findExactMethod(inv, cpg, methodChooser); } else { 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("[")) { // Java 1.5 allows array classes to appear as the class name className = "java.lang.Object"; } JavaClass jClass = Repository.lookupClass(className); return findInvocationLeastUpperBound( jClass, methodName, methodSig, methodChooser, opcode == Constants.INVOKEINTERFACE); } }
/** * 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<JavaClassAndMethod> resolveMethodCallTargets( ReferenceType receiverType, InvokeInstruction invokeInstruction, ConstantPoolGen cpg, boolean receiverTypeIsExact) throws ClassNotFoundException { HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>(); if (invokeInstruction.getOpcode() == Constants.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("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.<JavaClassAndMethod>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() == Constants.INVOKEVIRTUAL || invokeInstruction.getOpcode() == Constants.INVOKEINTERFACE) && (upperBound == null || !upperBound.getJavaClass().isFinal() && !upperBound.getMethod().isFinal()) && !receiverTypeIsExact; if (virtualCall) { if (!receiverClassName.equals("java.lang.Object")) { // This is a true virtual call: assume that any concrete // subtype method may be called. Set<ClassDescriptor> subTypeSet = analysisContext.getSubtypes2().getSubtypes(receiverDesc); for (ClassDescriptor subtype : subTypeSet) { XMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, false); if (concreteSubtypeMethod != null && (concreteSubtypeMethod.getAccessFlags() & Constants.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; }
@Override public String toString() { return SignatureConverter.convertMethodSignature(javaClass, method); }