/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2003-2008 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.jsr305;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import javax.annotation.meta.When;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InvokeInstruction;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.SignatureParser;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.ch.InheritanceGraphVisitor;
import edu.umd.cs.findbugs.ba.ch.OverriddenMethodsVisitor;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.UncheckedAnalysisException;
/**
* Find relevant type qualifiers needing to be checked for a given method.
*
* @author William Pugh
*/
public class Analysis {
private static final boolean DEBUG = SystemProperties.getBoolean("ctq.debug.analysis");
/**
* This system property enables additional work to try to detect all
* *effective* type qualifiers (direct, inherited, and default) applied to
* methods and called methods.
*
* This step uses an interprocedural call graph.
*/
public static final boolean FIND_EFFECTIVE_RELEVANT_QUALIFIERS = true; // SystemProperties.getBoolean("ctq.findeffective");
public static final boolean DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS = FIND_EFFECTIVE_RELEVANT_QUALIFIERS
&& SystemProperties.getBoolean("ctq.findeffective.debug");
/**
* Find relevant type qualifiers needing to be checked for a given method.
*
* @param methodDescriptor
* a method
* @return Collection of relevant type qualifiers needing to be checked
* @throws CheckedAnalysisException
*/
public static Collection<TypeQualifierValue<?>> getRelevantTypeQualifiers(MethodDescriptor methodDescriptor, CFG cfg)
throws CheckedAnalysisException {
final HashSet<TypeQualifierValue<?>> result = new HashSet<TypeQualifierValue<?>>();
XMethod xmethod = XFactory.createXMethod(methodDescriptor);
if (FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
if (DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
System.out.println("**** Finding effective type qualifiers for " + xmethod);
}
//
// This will take care of methods using fields annotated with
// a type qualifier.
//
getDirectlyRelevantTypeQualifiers(xmethod, result);
// For all known type qualifiers, find the effective (direct,
// inherited,
// or default) type qualifier annotations
// on the method and all methods directly called by the method.
//
addEffectiveRelevantQualifiers(result, xmethod);
IAnalysisCache analysisCache = Global.getAnalysisCache();
ConstantPoolGen cpg = analysisCache.getClassAnalysis(ConstantPoolGen.class, methodDescriptor.getClassDescriptor());
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
Instruction ins = location.getHandle().getInstruction();
if (ins instanceof InvokeInstruction) {
XMethod called = XFactory.createXMethod((InvokeInstruction) ins, cpg);
addEffectiveRelevantQualifiers(result, called);
}
if (DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
System.out.println("===> result: " + result);
}
}
//
// XXX: this code can go away eventually
//
if (!methodDescriptor.isStatic()) {
// Instance method - must consider type qualifiers inherited
// from superclasses
InheritanceGraphVisitor visitor = new OverriddenMethodsVisitor(xmethod) {
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.ch.OverriddenMethodsVisitor#
* visitOverriddenMethod(edu.umd.cs.findbugs.ba.XMethod)
*/
@Override
protected boolean visitOverriddenMethod(XMethod xmethod) {
getDirectlyRelevantTypeQualifiers(xmethod, result);
return true;
}
};
try {
AnalysisContext.currentAnalysisContext().getSubtypes2()
.traverseSupertypes(xmethod.getClassDescriptor(), visitor);
} catch (ClassNotFoundException e) {
AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
return Collections.<TypeQualifierValue<?>> emptySet();
} catch (UncheckedAnalysisException e) {
AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
.logError("Error getting relevant type qualifiers for " + xmethod.toString(), e);
return Collections.<TypeQualifierValue<?>> emptySet();
}
}
}
return result;
}
private static void addEffectiveRelevantQualifiers(HashSet<TypeQualifierValue<?>> result, XMethod xmethod) {
if (DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
System.out.println(" Finding effective qualifiers for " + xmethod);
}
for (TypeQualifierValue<?> tqv : TypeQualifierValue.getAllKnownTypeQualifiers()) {
if (DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
System.out.print(" " + tqv + "...");
}
TypeQualifierAnnotation tqa;
boolean add = false;
tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(xmethod, tqv);
if (tqa != null) {
add = true;
}
if (!add) {
int numParams = xmethod.getNumParams();
for (int i = 0; i < numParams; i++) {
tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(xmethod, i, tqv);
if (tqa != null) {
add = true;
break;
}
}
}
if (add) {
result.add(tqv);
}
if (DEBUG_FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
System.out.println(add ? "YES" : "NO");
}
}
}
// private static void propagateInheritedAnnotations() {
// // TODO
// }
private static void getDirectlyRelevantTypeQualifiers(XMethod xmethod, HashSet<TypeQualifierValue<?>> result) {
result.addAll(AnalysisContext.currentAnalysisContext().getDirectlyRelevantTypeQualifiersDatabase()
.getDirectlyRelevantTypeQualifiers(xmethod.getMethodDescriptor()));
}
/**
* @param result
* @param m
*/
public static void addKnownTypeQualifiersForParameters(HashSet<? super TypeQualifierValue<?>> result, XMethod m) {
int numParameters = new SignatureParser(m.getSignature()).getNumParameters();
for (int p = 0; p < numParameters; p++) {
addKnownTypeQualifiers(result, TypeQualifierApplications.getApplicableApplications(m, p));
}
}
/**
* @param result
* @param applicableApplications
*/
public static void addKnownTypeQualifiers(HashSet<? super TypeQualifierValue<?>> result,
Collection<TypeQualifierAnnotation> applicableApplications) {
for (TypeQualifierAnnotation t : applicableApplications) {
if (t.when != When.UNKNOWN) {
result.add(t.typeQualifier);
}
}
}
}