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;
021:
022: import java.util.ArrayList;
023: import java.util.BitSet;
024: import java.util.List;
025: import java.util.StringTokenizer;
026:
027: import org.apache.bcel.Constants;
028: import org.apache.bcel.classfile.ClassFormatException;
029: import org.apache.bcel.classfile.Constant;
030: import org.apache.bcel.classfile.ConstantMethodref;
031: import org.apache.bcel.classfile.ConstantNameAndType;
032: import org.apache.bcel.classfile.ConstantPool;
033: import org.apache.bcel.classfile.ConstantUtf8;
034: import org.apache.bcel.classfile.JavaClass;
035: import org.apache.bcel.generic.ConstantPoolGen;
036: import org.apache.bcel.generic.GETSTATIC;
037: import org.apache.bcel.generic.Instruction;
038: import org.apache.bcel.generic.InvokeInstruction;
039:
040: import edu.umd.cs.findbugs.SystemProperties;
041:
042: /**
043: * Mark methodref constant pool entries of methods
044: * that are likely to implement assertions.
045: * This is useful for pruning likely false paths.
046: *
047: * @author David Hovemeyer
048: */
049: public class AssertionMethods implements Constants {
050:
051: private static final boolean DEBUG = SystemProperties
052: .getBoolean("assertionmethods.debug");
053:
054: /**
055: * Bitset of methodref constant pool indexes referring to likely assertion methods.
056: */
057: private BitSet assertionMethodRefSet;
058:
059: private static class UserAssertionMethod {
060: private String className;
061: private String methodName;
062:
063: public UserAssertionMethod(String className, String methodName) {
064: this .className = className;
065: this .methodName = methodName;
066: }
067:
068: public String getClassName() {
069: return className;
070: }
071:
072: public String getMethodName() {
073: return methodName;
074: }
075: }
076:
077: private static final List<UserAssertionMethod> userAssertionMethodList = new ArrayList<UserAssertionMethod>();
078:
079: static {
080: String userProperty = SystemProperties
081: .getProperty("findbugs.assertionmethods");
082: if (userProperty != null) {
083: StringTokenizer tok = new StringTokenizer(userProperty, ",");
084: while (tok.hasMoreTokens()) {
085: String fullyQualifiedName = tok.nextToken();
086: int lastDot = fullyQualifiedName.lastIndexOf('.');
087: if (lastDot < 0)
088: continue;
089: String className = fullyQualifiedName.substring(0,
090: lastDot);
091: String methodName = fullyQualifiedName
092: .substring(lastDot + 1);
093: userAssertionMethodList.add(new UserAssertionMethod(
094: className, methodName));
095: }
096: }
097: }
098:
099: /**
100: * Constructor.
101: *
102: * @param jclass the JavaClass containing the methodrefs
103: */
104: public AssertionMethods(JavaClass jclass) {
105: this .assertionMethodRefSet = new BitSet();
106: init(jclass);
107: }
108:
109: private void init(JavaClass jclass) {
110: ConstantPool cp = jclass.getConstantPool();
111: int numConstants = cp.getLength();
112: for (int i = 0; i < numConstants; ++i) {
113: try {
114: Constant c = cp.getConstant(i);
115: if (c instanceof ConstantMethodref) {
116: ConstantMethodref cmr = (ConstantMethodref) c;
117: ConstantNameAndType cnat = (ConstantNameAndType) cp
118: .getConstant(cmr.getNameAndTypeIndex(),
119: CONSTANT_NameAndType);
120: String methodName = ((ConstantUtf8) cp.getConstant(
121: cnat.getNameIndex(), CONSTANT_Utf8))
122: .getBytes();
123: String className = cp.getConstantString(
124: cmr.getClassIndex(), CONSTANT_Class)
125: .replace('/', '.');
126: String methodSig = ((ConstantUtf8) cp.getConstant(
127: cnat.getSignatureIndex(), CONSTANT_Utf8))
128: .getBytes();
129:
130: String classNameLC = className.toLowerCase();
131: String methodNameLC = methodName.toLowerCase();
132: boolean voidReturnType = methodSig.endsWith(")V");
133:
134: if (DEBUG) {
135: System.out.print("Is " + className + "."
136: + methodName + " assertion method: "
137: + voidReturnType);
138: }
139:
140: if (isUserAssertionMethod(className, methodName)
141: || className.endsWith("Assert")
142: && methodName.startsWith("is")
143: || voidReturnType
144: && (classNameLC.indexOf("assert") >= 0
145: || methodNameLC.startsWith("throw")
146: || methodName.startsWith("affirm")
147: || methodName.startsWith("panic")
148: || methodName.equals("logTerminal")
149: || methodNameLC.equals("insist")
150: || methodNameLC.equals("usage")
151: || methodNameLC.equals("exit")
152: || methodNameLC.startsWith("fail")
153: || methodNameLC.startsWith("fatal")
154: || methodNameLC.indexOf("assert") >= 0
155: || methodNameLC.indexOf("legal") >= 0
156: || methodNameLC.indexOf("error") >= 0
157: || methodNameLC.indexOf("abort") >= 0
158: || methodNameLC.indexOf("check") >= 0 || methodNameLC
159: .indexOf("failed") >= 0)) {
160: assertionMethodRefSet.set(i);
161: if (DEBUG) {
162: System.out.println("==> YES");
163: }
164: } else {
165: if (DEBUG) {
166: System.out.println("==> NO");
167: }
168: }
169: }
170: } catch (ClassFormatException e) {
171: // FIXME: should report
172: }
173: }
174: }
175:
176: private static boolean isUserAssertionMethod(String className,
177: String methodName) {
178: for (UserAssertionMethod uam : userAssertionMethodList) {
179: if (className.equals(uam.getClassName())
180: && methodName.equals(uam.getMethodName()))
181: return true;
182: }
183: return false;
184: }
185:
186: /**
187: * Does the given InvokeInstruction refer to a likely assertion method?
188: *
189: * @param ins the InvokeInstruction
190: * @return true if the instruction likely refers to an assertion, false if not
191: */
192:
193: public boolean isAssertionInstruction(Instruction ins,
194: ConstantPoolGen cpg) {
195:
196: if (ins instanceof InvokeInstruction)
197: return isAssertionCall((InvokeInstruction) ins);
198: if (ins instanceof GETSTATIC) {
199: GETSTATIC getStatic = (GETSTATIC) ins;
200: String className = getStatic.getClassName(cpg);
201: String fieldName = getStatic.getFieldName(cpg);
202: if (className.equals("java.util.logging.Level")
203: && fieldName.equals("SEVERE"))
204: return true;
205: if (className.equals("org.apache.log4j.Level")
206: && (fieldName.equals("ERROR") || fieldName
207: .equals("FATAL")))
208: return true;
209: return false;
210:
211: }
212: return false;
213: }
214:
215: public boolean isAssertionCall(InvokeInstruction inv) {
216: // if (DEBUG) {
217: // System.out.print("Checking if " + inv + " is an assertion method: ");
218: // }
219: boolean isAssertionMethod = assertionMethodRefSet.get(inv
220: .getIndex());
221: // if (DEBUG) {
222: // System.out.println("==> " + isAssertionMethod);
223: // }
224: return isAssertionMethod;
225: }
226: }
227:
228: // vim:ts=4
|