📄 findrefcomparison.java
字号:
/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003-2005, 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.detect;import edu.umd.cs.findbugs.*;import edu.umd.cs.findbugs.ba.*;import edu.umd.cs.findbugs.ba.type.*;import edu.umd.cs.findbugs.props.*;import java.util.*;import org.apache.bcel.Constants;import org.apache.bcel.classfile.*;import org.apache.bcel.generic.*;/** * Find suspicious reference comparisons. * This includes: * <ul> * <li>Strings and other java.lang objects compared by reference equality</li> * <li>Calls to equals(Object) where the argument is a different type than * the receiver object</li> * </ul> * * @author David Hovemeyer * @author Bill Pugh */public class FindRefComparison implements Detector, ExtendedTypes { private static final boolean DEBUG = SystemProperties.getBoolean("frc.debug"); private static final boolean REPORT_ALL_REF_COMPARISONS = SystemProperties.getBoolean("findbugs.refcomp.reportAll"); private static final int BASE_ES_PRIORITY = SystemProperties.getInteger("es.basePriority", NORMAL_PRIORITY); /** * Classes that are suspicious if compared by reference. */ private static final HashSet<String> suspiciousSet = new HashSet<String>(); static { suspiciousSet.add("java.lang.Boolean"); suspiciousSet.add("java.lang.Byte"); suspiciousSet.add("java.lang.Character"); suspiciousSet.add("java.lang.Double"); suspiciousSet.add("java.lang.Float"); suspiciousSet.add("java.lang.Integer"); suspiciousSet.add("java.lang.Long"); suspiciousSet.add("java.lang.Short"); } /** * Set of opcodes that invoke instance methods on an object. */ private static final BitSet invokeInstanceSet = new BitSet(); static { invokeInstanceSet.set(Constants.INVOKEVIRTUAL); invokeInstanceSet.set(Constants.INVOKEINTERFACE); invokeInstanceSet.set(Constants.INVOKESPECIAL); } /** * Set of bytecodes using for prescreening. */ private static final BitSet prescreenSet = new BitSet(); static { prescreenSet.or(invokeInstanceSet); prescreenSet.set(Constants.IF_ACMPEQ); prescreenSet.set(Constants.IF_ACMPNE); } /* ---------------------------------------------------------------------- * Helper classes * ---------------------------------------------------------------------- */ private static final byte T_DYNAMIC_STRING = T_AVAIL_TYPE + 0; private static final byte T_STATIC_STRING = T_AVAIL_TYPE + 1; private static final byte T_PARAMETER_STRING = T_AVAIL_TYPE + 2; private static final String STRING_SIGNATURE = "Ljava/lang/String;"; /** * Type representing a dynamically created String. * This sort of String should never be compared using reference * equality. */ private static class DynamicStringType extends ObjectType { private static final long serialVersionUID = 1L; public DynamicStringType() { super("java.lang.String"); } @Override public byte getType() { return T_DYNAMIC_STRING; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public boolean equals(Object o) { return o == this; } @Override public String toString() { return "<dynamic string>"; } } private static final Type dynamicStringTypeInstance = new DynamicStringType(); /** * Type representing a static String. * E.g., interned strings and constant strings. * It is generally OK to compare this sort of String * using reference equality. */ private static class StaticStringType extends ObjectType { private static final long serialVersionUID = 1L; public StaticStringType() { super("java.lang.String"); } @Override public byte getType() { return T_STATIC_STRING; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public boolean equals(Object o) { return o == this; } @Override public String toString() { return "<static string>"; } } private static final Type staticStringTypeInstance = new StaticStringType(); /** * Type representing a String passed as a parameter. */ private static class ParameterStringType extends ObjectType { private static final long serialVersionUID = 1L; public ParameterStringType() { super("java.lang.String"); } @Override public byte getType() { return T_PARAMETER_STRING; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public boolean equals(Object o) { return o == this; } @Override public String toString() { return "<parameter string>"; } } private static final Type parameterStringTypeInstance = new ParameterStringType(); private static class RefComparisonTypeFrameModelingVisitor extends TypeFrameModelingVisitor { private RepositoryLookupFailureCallback lookupFailureCallback; private boolean sawStringIntern; public RefComparisonTypeFrameModelingVisitor( ConstantPoolGen cpg, RepositoryLookupFailureCallback lookupFailureCallback) { super(cpg); this.lookupFailureCallback = lookupFailureCallback; this.sawStringIntern = false; } public boolean sawStringIntern() { return sawStringIntern; } // Override handlers for bytecodes that may return String objects // known to be dynamic or static. @Override public void visitINVOKESTATIC(INVOKESTATIC obj) { consumeStack(obj); if (returnsString(obj)) { String className = obj.getClassName(getCPG()); if (className.equals("java.lang.String")) { pushValue(dynamicStringTypeInstance); } else { pushReturnType(obj); } } else { pushReturnType(obj); } } @Override public void visitINVOKESPECIAL(INVOKESPECIAL obj) { handleInstanceMethod(obj); } @Override public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) { handleInstanceMethod(obj); } @Override public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) { handleInstanceMethod(obj); } private boolean returnsString(InvokeInstruction inv) { String methodSig = inv.getSignature(getCPG()); return methodSig.endsWith(")Ljava/lang/String;"); } private void handleInstanceMethod(InvokeInstruction obj) { consumeStack(obj); if (returnsString(obj)) { String className = obj.getClassName(getCPG()); String methodName = obj.getName(getCPG()); // System.out.println(className + "." + methodName); if (methodName.equals("intern") && className.equals("java.lang.String")) { sawStringIntern = true; pushValue(staticStringTypeInstance); } else if (methodName.equals("toString") || className.equals("java.lang.String")) { pushValue(dynamicStringTypeInstance); // System.out.println(" dynamic"); } else pushReturnType(obj); } else pushReturnType(obj); } @Override public void visitLDC(LDC obj) { Type type = obj.getType(getCPG()); pushValue(isString(type) ? staticStringTypeInstance : type); } @Override public void visitLDC2_W(LDC2_W obj) { Type type = obj.getType(getCPG()); pushValue(isString(type) ? staticStringTypeInstance : type); } private boolean isString(Type type) { return type.getSignature().equals(STRING_SIGNATURE); } @Override public void visitGETSTATIC(GETSTATIC obj) { handleLoad(obj); } @Override public void visitGETFIELD(GETFIELD obj) { handleLoad(obj); } private void handleLoad(FieldInstruction obj) { consumeStack(obj); Type type = obj.getType(getCPG()); if (type.getSignature().equals(STRING_SIGNATURE)) { try { String className = obj.getClassName(getCPG()); String fieldName = obj.getName(getCPG()); Field field = Hierarchy.findField(className, fieldName); if (field != null) { // If the field is final, we'll assume that the String value // is static. if (field.isFinal()) pushValue(staticStringTypeInstance); else pushValue(type); return; } } catch (ClassNotFoundException ex) { lookupFailureCallback.reportMissingClass(ex); } } pushValue(type); } } /** * Type merger to use the extended String types. */ private static class RefComparisonTypeMerger extends StandardTypeMerger { public RefComparisonTypeMerger(RepositoryLookupFailureCallback lookupFailureCallback, ExceptionSetFactory exceptionSetFactory) { super(lookupFailureCallback, exceptionSetFactory); } @Override protected boolean isReferenceType(byte type) { return super.isReferenceType(type) || type == T_STATIC_STRING || type == T_DYNAMIC_STRING; } @Override protected Type mergeReferenceTypes(ReferenceType aRef, ReferenceType bRef) throws DataflowAnalysisException { byte aType = aRef.getType(); byte bType = bRef.getType(); if (isExtendedStringType(aType) || isExtendedStringType(bType)) { // If both types are the same extended String type, // then the same type is returned. Otherwise, extended // types are downgraded to plain java.lang.String, // and a standard merge is applied. if (aType == bType) return aRef; if (isExtendedStringType(aType)) aRef = Type.STRING; if (isExtendedStringType(bType)) bRef = Type.STRING; } return super.mergeReferenceTypes(aRef, bRef); } private boolean isExtendedStringType(byte type) { return type == T_DYNAMIC_STRING || type == T_STATIC_STRING || type == T_PARAMETER_STRING; } } /* ---------------------------------------------------------------------- * Fields * ---------------------------------------------------------------------- */ private BugReporter bugReporter; private ClassContext classContext; /* ---------------------------------------------------------------------- * Implementation * ---------------------------------------------------------------------- */ public FindRefComparison(BugReporter bugReporter) { this.bugReporter = bugReporter; } public void visitClassContext(ClassContext classContext) { this.classContext = classContext; JavaClass jclass = classContext.getJavaClass(); Method[] methodList = jclass.getMethods(); for (Method method : methodList) { MethodGen methodGen = classContext.getMethodGen(method); if (methodGen == null) continue; // Prescreening - must have IF_ACMPEQ, IF_ACMPNE, // or an invocation of an instance method BitSet bytecodeSet = classContext.getBytecodeSet(method); if (bytecodeSet == null || !bytecodeSet.intersects(prescreenSet)) continue; if (DEBUG) System.out.println("FindRefComparison: analyzing " + SignatureConverter.convertMethodSignature(methodGen)); try { analyzeMethod(classContext, method); } catch (CFGBuilderException e) { bugReporter.logError("Error analyzing " + method.toString(), e); } catch (DataflowAnalysisException e) { // bugReporter.logError("Error analyzing " + method.toString(), e); } } } /** * A BugInstance and its WarningPropertySet. */ private static class WarningWithProperties { BugInstance instance; WarningPropertySet propertySet; Location location; WarningWithProperties(BugInstance warning, WarningPropertySet propertySet, Location location) { this.instance = warning;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -