[Fb-contrib-commit] SF.net SVN: fb-contrib: [887] trunk/fb-contrib
Brought to you by:
dbrosius
From: <dbr...@us...> - 2007-06-24 22:58:04
|
Revision: 887 http://svn.sourceforge.net/fb-contrib/?rev=887&view=rev Author: dbrosius Date: 2007-06-24 15:58:05 -0700 (Sun, 24 Jun 2007) Log Message: ----------- Initial checkin of UAA - doesn't work a lick Modified Paths: -------------- trunk/fb-contrib/etc/findbugs.xml trunk/fb-contrib/etc/messages.xml Added Paths: ----------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java Modified: trunk/fb-contrib/etc/findbugs.xml =================================================================== --- trunk/fb-contrib/etc/findbugs.xml 2007-06-24 22:23:02 UTC (rev 886) +++ trunk/fb-contrib/etc/findbugs.xml 2007-06-24 22:58:05 UTC (rev 887) @@ -277,10 +277,11 @@ <Detector class="com.mebigfatguy.fbcontrib.detect.SuspiciousJDKVersionUse" speed="slow" - reports="SJVU_SUSPICIOUS_JDK_VERSION_USE" - hidden="true" - disabled="true" /> + reports="SJVU_SUSPICIOUS_JDK_VERSION_USE" /> + <Detector class="com.mebigfatguy.fbcontrib.detect.UseAddAll" + speed="fast" + reports="UAA_USE_ADD_ALL" /> <!-- BugPattern --> @@ -370,4 +371,5 @@ <BugPattern abbrev="DWI" type="DWI_MODIFYING_WHILE_ITERATING" category="CORRECTNESS" experimental="true" /> <BugPattern abbrev="USS" type="USS_USE_STRING_SPLIT" category="STYLE" /> <BugPattern abbrev="SJVU" type="SJVU_SUSPICIOUS_JDK_VERSION_USE" category="CORRECTNESS" experimental="true" /> + <BugPattern abbrev="UAA" type="UAA_USE_ADD_ALL" category="STYLE" experimental="true" /> </FindbugsPlugin> \ No newline at end of file Modified: trunk/fb-contrib/etc/messages.xml =================================================================== --- trunk/fb-contrib/etc/messages.xml 2007-06-24 22:23:02 UTC (rev 886) +++ trunk/fb-contrib/etc/messages.xml 2007-06-24 22:58:05 UTC (rev 887) @@ -754,6 +754,18 @@ ]]> </Details> </Detector> + + <Detector class="com.mebigfatguy.fbcontrib.detect.UseAddAll"> + <Details> + <![CDATA[ + <p>looks for loops that transfers the contents of one collection to another. These collection sources might + be local variables or member fields, including sets, maps key/values, lists, or arrays. It is simpler to + just use the addAll method of the collection class. In the case where the source is an array, you can use + Arrays.asList(array), and use that as the source to addAll.</p> + <p>It is a fast detector.</p> + ]]> + </Details> + </Detector> <!-- BugPattern --> @@ -1897,6 +1909,19 @@ </Details> </BugPattern> + <BugPattern type="UAA_USE_ADD_ALL"> + <ShortDescription>method uses simple loop to copy contents of one collection to another</ShortDescription> + <LongDescription>method {1} uses simple loop to copy contents of one colleciton to another</LongDescription> + <Details> + <![CDATA[ + <p>This method uses a simple for loop to copy the contents of a set, list, map key/value, array or other collection + to another collection. It is simpler and more straight forward to just call the addAll method of the destination collection + passing in the source collection. In the case that the source is an array, you can use Array.asList method to massage the array + into a collection</p> + ]]> + </Details> + </BugPattern> + <!-- BugCode --> <BugCode abbrev="ISB">Inefficient String Buffering</BugCode> @@ -1961,4 +1986,5 @@ <BugCode abbrev="DWI">Deleting While Iterating</BugCode> <BugCode abbrev="USS">Use String Split</BugCode> <BugCode abbrev="SJVU">Suspicious JDK Version Use</BugCode> + <BugCode abbrev="UAA">Use Add All</BugCode> </MessageCollection> \ No newline at end of file Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java (rev 0) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java 2007-06-24 22:58:05 UTC (rev 887) @@ -0,0 +1,218 @@ +package com.mebigfatguy.fbcontrib.detect; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.bcel.Repository; +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.JavaClass; + +import com.mebigfatguy.fbcontrib.utils.Integer14; +import com.mebigfatguy.fbcontrib.utils.RegisterUtils; + +import edu.umd.cs.findbugs.BugInstance; +import edu.umd.cs.findbugs.BugReporter; +import edu.umd.cs.findbugs.BytecodeScanningDetector; +import edu.umd.cs.findbugs.OpcodeStack; +import edu.umd.cs.findbugs.ba.ClassContext; + +/** + * looks for loops that transfers the contents of one collection to another. These collection sources + * might be local variables or member fields, including sets, maps key/values, lists, or arrays. + * It is simpler to just use the addAll method of the collection class. In the case where the + * source is an array, you can use Arrays.asList(array), and use that as the source to addAll. + */ +public class UseAddAll extends BytecodeScanningDetector { + private static JavaClass collectionClass; + private static ClassNotFoundException ex; + static { + try { + collectionClass = Repository.lookupClass("java/util/Collection"); + } catch (ClassNotFoundException cnfe) { + collectionClass = null; + ex = cnfe; + } + } + + private BugReporter bugReporter; + private OpcodeStack stack; + private Map<Integer, Object> userValues; + + /** + * constructs a UTA detector given the reporter to report bugs on + * @param bugReporter the sync of bug reports + */ + public UseAddAll(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + /** + * implements the visitor to create and clear the stack, and report missing class errors + * + * @param classContext the context object of the currently parsed class + */ + @Override + public void visitClassContext(ClassContext classContext) { + if (collectionClass == null) { + if (ex != null) { + bugReporter.reportMissingClass(ex); + ex = null; + } + return; + } + + try { + stack = new OpcodeStack(); + super.visitClassContext(classContext); + } finally { + stack = null; + } + } + + /** + * implements the visitor to reset the stack and uservalues + * + * @param obj the context object of the currently parsed code block + */ + @Override + public void visitCode(Code obj) { + try { + stack.resetForMethodEntry(this); + userValues = new HashMap<Integer, Object>(); + super.visitCode(obj); + } finally { + userValues = null; + } + } + + /** + * implements the visitor to look for manually copying of collections to collections + * + * @param seen the opcode of the currently parsed instruction + */ + @Override + public void sawOpcode(int seen) { + int reg = -1; + Object uValue = null; + boolean sawAlias = false; + boolean sawLoad = false; + + try { + if (seen == INVOKEINTERFACE) { + String methodName = getNameConstantOperand(); + String signature = getSigConstantOperand(); + if ("size".equals(methodName) && "()I".equals(signature)) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + reg = isLocalCollection(itm); + if (reg >= 0) { + sawAlias = true; + } + } + } else if ("get".equals(methodName) && "(I)Ljava/lang/Object;".equals(signature)) { + if (stack.getStackDepth() > 1) { + OpcodeStack.Item itm = stack.getStackItem(1); + reg = isLocalCollection(itm); + if (reg >= 0) { + sawAlias = true; + } + } + } else if ("keySet".equals(methodName) || "values".equals(methodName) || "iterator".equals(methodName) || "next".equals(methodName)) { + if (stack.getStackDepth() > 1) { + OpcodeStack.Item itm = stack.getStackItem(1); + reg = isLocalCollection(itm); + if (reg >= 0) { + sawAlias = true; + } + } + } else if ("add".equals(methodName) && "(Ljava/lang/Object;)Z".equals(signature)) { + if (stack.getStackDepth() > 1) { + OpcodeStack.Item colItem = stack.getStackItem(1); + OpcodeStack.Item valueItem = stack.getStackItem(0); + reg = isLocalCollection(colItem); + if ((reg >= 0) + && (valueItem.getUserValue() != null)) { + bugReporter.reportBug(new BugInstance(this, "UTA_USE_ADD_ALL", NORMAL_PRIORITY) + .addClass(this) + .addMethod(this) + .addSourceLine(this)); + } + } + } + } else if (((seen == ISTORE) || ((seen >= ISTORE_0) && (seen <= ISTORE_3))) + || ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3)))) { + if (stack.getStackDepth() > 0) { + uValue = stack.getStackItem(0).getUserValue(); + userValues.put(Integer14.valueOf(RegisterUtils.getStoreReg(this, seen)), uValue); + } + } else if (((seen == ILOAD) || ((seen >= ILOAD_0) && (seen <= ILOAD_3))) + || ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3)))) { + sawLoad = true; + } else if (seen == IF_ICMPGE) { + if (stack.getStackDepth() > 1) { + OpcodeStack.Item itm1 = stack.getStackItem(1); + OpcodeStack.Item itm2 = stack.getStackItem(0); + reg = itm1.getRegisterNumber(); + if ((reg >= 0) && (itm1.couldBeZero())) { + uValue = itm2.getUserValue(); + if (uValue != null) { + userValues.put(Integer14.valueOf(reg), uValue); + } + } + } + } else if (seen == CHECKCAST) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + uValue = itm.getUserValue(); + if (uValue instanceof Integer) { + reg = ((Integer)uValue).intValue(); + sawAlias = true; + } + } + } + } catch (ClassNotFoundException cnfe) { + bugReporter.reportMissingClass(cnfe); + } finally { + stack.sawOpcode(this, seen); + if (sawAlias) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + itm.setUserValue(Integer14.valueOf(reg)); + } + } else if (sawLoad) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + reg = itm.getRegisterNumber(); + if (reg >= 0) { + uValue = userValues.get(Integer14.valueOf(reg)); + itm.setUserValue(uValue); + } + } + } + } + } + + /** + * determines if the stack item refers to a collection that is stored in a local variable + * + * param item the stack item to check + * + * @return the register number of the local variable that this collection refers to, or -1 + * @throws ClassNotFoundException if the items class cannot be found + */ + private int isLocalCollection(OpcodeStack.Item item) throws ClassNotFoundException { + Integer aliasReg = (Integer)item.getUserValue(); + if (aliasReg != null) + return aliasReg.intValue(); + + int reg = item.getRegisterNumber(); + if (reg < 0) + return -1; + + JavaClass cls = item.getJavaClass(); + if ((cls != null) && cls.implementationOf(collectionClass)) + return reg; + + return -1; + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |