📄 findrefcomparison.java
字号:
/* * FindBugs - Find bugs in Java programs * 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.detect;import java.util.*;import edu.umd.cs.findbugs.BugInstance;import edu.umd.cs.findbugs.BugReporter;import edu.umd.cs.findbugs.Detector;import edu.umd.cs.findbugs.ba.*;import org.apache.bcel.Constants;import org.apache.bcel.classfile.Field;import org.apache.bcel.classfile.JavaClass;import org.apache.bcel.classfile.Method;import org.apache.bcel.generic.*;public class FindRefComparison implements Detector, ExtendedTypes { private static final boolean DEBUG = Boolean.getBoolean("frc.debug"); private static final boolean REPORT_ALL_REF_COMPARISONS = Boolean.getBoolean("findbugs.refcomp.reportAll"); /** * 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 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 { public DynamicStringType() { super("java.lang.String"); } public byte getType() { return T_DYNAMIC_STRING; } public int hashCode() { return System.identityHashCode(this); } public boolean equals(Object o) { return o == this; } 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 { public StaticStringType() { super("java.lang.String"); } public byte getType() { return T_STATIC_STRING; } public int hashCode() { return System.identityHashCode(this); } public boolean equals(Object o) { return o == this; } public String toString() { return "<static string>"; } } private static final Type staticStringTypeInstance = new StaticStringType(); private static class RefComparisonTypeFrameModelingVisitor extends TypeFrameModelingVisitor { private RepositoryLookupFailureCallback lookupFailureCallback; public RefComparisonTypeFrameModelingVisitor(ConstantPoolGen cpg, RepositoryLookupFailureCallback lookupFailureCallback) { super(cpg); this.lookupFailureCallback = lookupFailureCallback; } // Override handlers for bytecodes that may return String objects // known to be dynamic or static. 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); } } public void visitINVOKESPECIAL(INVOKESPECIAL obj) { handleInstanceMethod(obj); } public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) { handleInstanceMethod(obj); } 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); } public void visitLDC(LDC obj) { Type type = obj.getType(getCPG()); pushValue(isString(type) ? staticStringTypeInstance : type); } 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); } public void visitGETSTATIC(GETSTATIC obj) { handleLoad(obj); } 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); } protected boolean isReferenceType(byte type) { return super.isReferenceType(type) || type == T_STATIC_STRING || type == T_DYNAMIC_STRING; } 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; } } /* ---------------------------------------------------------------------- * Fields * ---------------------------------------------------------------------- */ private BugReporter bugReporter; //private AnalysisContext analysisContext; private BugInstance stringComparison; private BugInstance refComparison; /* ---------------------------------------------------------------------- * Implementation * ---------------------------------------------------------------------- */ public FindRefComparison(BugReporter bugReporter) { this.bugReporter = bugReporter; } public void setAnalysisContext(AnalysisContext analysisContext) { //this.analysisContext = analysisContext; } // XXX BAD EVIL NOT THREAD SAFE YUCK FIXME
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -