edu.umd.cs.findbugs.ba.bcp.Invoke Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* 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.bcp;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.apache.bcel.Const;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.Hierarchy;
import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
/**
* A PatternElement to match a method invocation. Currently, we don't allow
* variables in this element (for arguments and return value). This would be a
* good thing to add. We also don't distinguish between invokevirtual,
* invokeinterface, and invokespecial.
*
*
* Invoke objects match by class name, method name, method signature, and
* mode.
*
*
* Names and signatures may be matched in several ways:
*
* - By an exact match. This is the default behavior.
* - By a regular expression. If the string provided to the Invoke constructor
* begins with a "/" character, the rest of the string is treated as a regular
* expression.
* - As a subclass match. This only applies to class name matches. If the
* first character of a class name string is "+", then the rest of the string is
* treated as the name of a base class. Any subclass or subinterface of the
* named type will be accepted.
*
*
* The mode specifies what kind of invocations in the Invoke element
* matches. It is specified as the bitwise combination of the following values:
*
* -
INSTANCE
, which matches ordinary instance method invocations
* -
STATIC
, which matches static method invocations
* -
CONSTRUCTOR
, which matches object constructor invocations
*
* The special mode ORDINARY_METHOD
is equivalent to
* INSTANCE|STATIC
. The special mode ANY
is equivalent
* to INSTANCE|STATIC|CONSTRUCTOR
.
*
* @author David Hovemeyer
* @see PatternElement
*/
public class Invoke extends PatternElement {
/**
* Match ordinary (non-constructor) instance invocations.
*/
public static final int INSTANCE = 1;
/**
* Match static invocations.
*/
public static final int STATIC = 2;
/**
* Match object constructor invocations.
*/
public static final int CONSTRUCTOR = 4;
/**
* Match ordinary methods (everything except constructors).
*/
public static final int ORDINARY_METHOD = INSTANCE | STATIC;
/**
* Match both static and instance invocations.
*/
public static final int ANY = INSTANCE | STATIC | CONSTRUCTOR;
private interface StringMatcher {
public boolean match(String s);
}
private static class ExactStringMatcher implements StringMatcher {
private final String value;
public ExactStringMatcher(String value) {
this.value = value;
}
@Override
public boolean match(String s) {
return s.equals(value);
}
}
private static class RegexpStringMatcher implements StringMatcher {
private final Pattern pattern;
public RegexpStringMatcher(String re) {
pattern = Pattern.compile(re);
}
@Override
public boolean match(String s) {
return pattern.matcher(s).matches();
}
}
private static class SubclassMatcher implements StringMatcher {
private final String className;
public SubclassMatcher(String className) {
this.className = className;
}
@Override
public boolean match(String s) {
try {
return Hierarchy.isSubtype(s, className);
} catch (ClassNotFoundException e) {
AnalysisContext.reportMissingClass(e);
return false;
}
}
}
private final StringMatcher classNameMatcher;
private final StringMatcher methodNameMatcher;
private final StringMatcher methodSigMatcher;
private final int mode;
/**
* Constructor.
*
* @param className
* the class name of the method; may be specified exactly, as a
* regexp, or as a subtype match
* @param methodName
* the name of the method; may be specified exactly or as a
* regexp
* @param methodSig
* the signature of the method; may be specified exactly or as a
* regexp
* @param mode
* the mode of invocation
*/
public Invoke(String className, String methodName, String methodSig, int mode,
@Nullable RepositoryLookupFailureCallback lookupFailureCallback) {
this.classNameMatcher = createClassMatcher(className);
this.methodNameMatcher = createMatcher(methodName);
this.methodSigMatcher = createMatcher(methodSig);
this.mode = mode;
}
private StringMatcher createClassMatcher(String s) {
return s.startsWith("+") ? new SubclassMatcher(s.substring(1)) : createMatcher(s);
}
private StringMatcher createMatcher(String s) {
return s.startsWith("/") ? (StringMatcher) new RegexpStringMatcher(s.substring(1))
: (StringMatcher) new ExactStringMatcher(s);
}
@Override
public MatchResult match(InstructionHandle handle, ConstantPoolGen cpg, ValueNumberFrame before, ValueNumberFrame after,
BindingSet bindingSet) throws DataflowAnalysisException {
// See if the instruction is an InvokeInstruction
Instruction ins = handle.getInstruction();
if (!(ins instanceof InvokeInstruction)) {
return null;
}
InvokeInstruction inv = (InvokeInstruction) ins;
String methodName = inv.getMethodName(cpg);
boolean isStatic = inv.getOpcode() == Const.INVOKESTATIC;
boolean isCtor = Const.CONSTRUCTOR_NAME.equals(methodName);
int actualMode = 0;
if (isStatic) {
actualMode |= STATIC;
}
if (isCtor) {
actualMode |= CONSTRUCTOR;
}
if (!isStatic && !isCtor) {
actualMode |= INSTANCE;
}
// Intersection of actual and desired modes must be nonempty.
if ((actualMode & mode) == 0) {
return null;
}
// Check class name, method name, and method signature.
if (!methodNameMatcher.match(methodName) || !methodSigMatcher.match(inv.getSignature(cpg))
|| !classNameMatcher.match(inv.getClassName(cpg))) {
return null;
}
// It's a match!
return new MatchResult(this, bindingSet);
}
@Override
public boolean acceptBranch(Edge edge, InstructionHandle source) {
return true;
}
@Override
public int minOccur() {
return 1;
}
@Override
public int maxOccur() {
return 1;
}
}