001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003,2004 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba.bcp;
021:
022: import java.util.regex.Pattern;
023:
024: import org.apache.bcel.Constants;
025: import org.apache.bcel.generic.ConstantPoolGen;
026: import org.apache.bcel.generic.Instruction;
027: import org.apache.bcel.generic.InstructionHandle;
028: import org.apache.bcel.generic.InvokeInstruction;
029:
030: import edu.umd.cs.findbugs.annotations.Nullable;
031: import edu.umd.cs.findbugs.ba.AnalysisContext;
032: import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
033: import edu.umd.cs.findbugs.ba.Edge;
034: import edu.umd.cs.findbugs.ba.Hierarchy;
035: import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
036: import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
037:
038: /**
039: * A PatternElement to match a method invocation.
040: * Currently, we don't allow variables in this element (for arguments
041: * and return value). This would be a good thing to add.
042: * We also don't distinguish between invokevirtual, invokeinterface,
043: * and invokespecial.
044: * <p/>
045: * <p> Invoke objects match by class name, method name, method signature,
046: * and <em>mode</em>.
047: * <p/>
048: * <p> Names and signatures may be matched in several ways:
049: * <ol>
050: * <li> By an exact match. This is the default behavior.
051: * <li> By a regular expression. If the string provided to the Invoke
052: * constructor begins with a "/" character, the rest of the string
053: * is treated as a regular expression.
054: * <li> As a subclass match. This only applies to class name matches.
055: * If the first character of a class name string is "+", then the
056: * rest of the string is treated as the name of a base class.
057: * Any subclass or subinterface of the named type will be accepted.
058: * </ol>
059: * <p/>
060: * <p> The <em>mode</em> specifies what kind of invocations in the Invoke
061: * element matches. It is specified as the bitwise combination of the
062: * following values:
063: * <ol>
064: * <li> <code>INSTANCE</code>, which matches ordinary instance method invocations
065: * <li> <code>STATIC</code>, which matches static method invocations
066: * <li> <code>CONSTRUCTOR</code>, which matches object constructor invocations
067: * </ol>
068: * The special mode <code>ORDINARY_METHOD</code> is equivalent to <code>INSTANCE|STATIC</code>.
069: * The special mode <code>ANY</code> is equivalent to <code>INSTANCE|STATIC|CONSTRUCTOR</code>.
070: *
071: * @author David Hovemeyer
072: * @see PatternElement
073: */
074: public class Invoke extends PatternElement {
075:
076: /**
077: * Match ordinary (non-constructor) instance invocations.
078: */
079: public static final int INSTANCE = 1;
080:
081: /**
082: * Match static invocations.
083: */
084: public static final int STATIC = 2;
085:
086: /**
087: * Match object constructor invocations.
088: */
089: public static final int CONSTRUCTOR = 4;
090:
091: /**
092: * Match ordinary methods (everything except constructors).
093: */
094: public static final int ORDINARY_METHOD = INSTANCE | STATIC;
095:
096: /**
097: * Match both static and instance invocations.
098: */
099: public static final int ANY = INSTANCE | STATIC | CONSTRUCTOR;
100:
101: private interface StringMatcher {
102: public boolean match(String s);
103: }
104:
105: private static class ExactStringMatcher implements StringMatcher {
106: private String value;
107:
108: public ExactStringMatcher(String value) {
109: this .value = value;
110: }
111:
112: public boolean match(String s) {
113: return s.equals(value);
114: }
115: }
116:
117: private static class RegexpStringMatcher implements StringMatcher {
118: private Pattern pattern;
119:
120: public RegexpStringMatcher(String re) {
121: pattern = Pattern.compile(re);
122: }
123:
124: public boolean match(String s) {
125: return pattern.matcher(s).matches();
126: }
127: }
128:
129: private static class SubclassMatcher implements StringMatcher {
130: private String className;
131:
132: public SubclassMatcher(String className) {
133: this .className = className;
134: }
135:
136: public boolean match(String s) {
137: try {
138: return Hierarchy.isSubtype(s, className);
139: } catch (ClassNotFoundException e) {
140: AnalysisContext.reportMissingClass(e);
141: return false;
142: }
143: }
144: }
145:
146: private final StringMatcher classNameMatcher;
147: private final StringMatcher methodNameMatcher;
148: private final StringMatcher methodSigMatcher;
149: private final int mode;
150:
151: /**
152: * Constructor.
153: *
154: * @param className the class name of the method; may be specified exactly,
155: * as a regexp, or as a subtype match
156: * @param methodName the name of the method; may be specified exactly or as a regexp
157: * @param methodSig the signature of the method; may be specified exactly or as a regexp
158: * @param mode the mode of invocation
159: */
160: public Invoke(String className, String methodName,
161: String methodSig, int mode, @Nullable
162: RepositoryLookupFailureCallback lookupFailureCallback) {
163: this .classNameMatcher = createClassMatcher(className);
164: this .methodNameMatcher = createMatcher(methodName);
165: this .methodSigMatcher = createMatcher(methodSig);
166: this .mode = mode;
167: }
168:
169: private StringMatcher createClassMatcher(String s) {
170: return s.startsWith("+") ? new SubclassMatcher(s.substring(1))
171: : createMatcher(s);
172: }
173:
174: private StringMatcher createMatcher(String s) {
175: return s.startsWith("/") ? (StringMatcher) new RegexpStringMatcher(
176: s.substring(1))
177: : (StringMatcher) new ExactStringMatcher(s);
178: }
179:
180: @Override
181: public MatchResult match(InstructionHandle handle,
182: ConstantPoolGen cpg, ValueNumberFrame before,
183: ValueNumberFrame after, BindingSet bindingSet)
184: throws DataflowAnalysisException {
185:
186: // See if the instruction is an InvokeInstruction
187: Instruction ins = handle.getInstruction();
188: if (!(ins instanceof InvokeInstruction))
189: return null;
190: InvokeInstruction inv = (InvokeInstruction) ins;
191:
192: String methodName = inv.getMethodName(cpg);
193: boolean isStatic = inv.getOpcode() == Constants.INVOKESTATIC;
194: boolean isCtor = methodName.equals("<init>");
195:
196: int actualMode = 0;
197:
198: if (isStatic)
199: actualMode |= STATIC;
200: if (isCtor)
201: actualMode |= CONSTRUCTOR;
202: if (!isStatic && !isCtor)
203: actualMode |= INSTANCE;
204:
205: // Intersection of actual and desired modes must be nonempty.
206: if ((actualMode & mode) == 0)
207: return null;
208:
209: // Check class name, method name, and method signature.
210: if (!methodNameMatcher.match(methodName)
211: || !methodSigMatcher.match(inv.getSignature(cpg))
212: || !classNameMatcher.match(inv.getClassName(cpg)))
213: return null;
214:
215: // It's a match!
216: return new MatchResult(this , bindingSet);
217:
218: }
219:
220: @Override
221: public boolean acceptBranch(Edge edge, InstructionHandle source) {
222: return true;
223: }
224:
225: @Override
226: public int minOccur() {
227: return 1;
228: }
229:
230: @Override
231: public int maxOccur() {
232: return 1;
233: }
234: }
235:
236: // vim:ts=4
|