[Fb-contrib-commit] SF.net SVN: fb-contrib: [737] trunk/fb-contrib
Brought to you by:
dbrosius
From: <dbr...@us...> - 2006-12-19 06:57:29
|
Revision: 737 http://svn.sourceforge.net/fb-contrib/?rev=737&view=rev Author: dbrosius Date: 2006-12-18 22:57:27 -0800 (Mon, 18 Dec 2006) Log Message: ----------- Initial checkin DWI detector: non functional at present Modified Paths: -------------- trunk/fb-contrib/etc/findbugs.xml trunk/fb-contrib/etc/messages.xml Added Paths: ----------- trunk/fb-contrib/samples/DWI_Sample.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java Modified: trunk/fb-contrib/etc/findbugs.xml =================================================================== --- trunk/fb-contrib/etc/findbugs.xml 2006-12-17 07:53:07 UTC (rev 736) +++ trunk/fb-contrib/etc/findbugs.xml 2006-12-19 06:57:27 UTC (rev 737) @@ -266,6 +266,10 @@ speed="fast" reports="SCI_SPOILED_CHILD_INTERFACE_IMPLEMENTOR" /> + <Detector class="com.mebigfatguy.fbcontrib.detect.DeletingWhileIterating" + speed="fast" + reports="DWI_DELETING_WHILE_ITERATING" /> + <!-- BugPattern --> <BugPattern abbrev="ISB" type="ISB_INEFFICIENT_STRING_BUFFERING" category="PERFORMANCE" /> @@ -341,4 +345,5 @@ <BugPattern abbrev="SPP" type="SPP_STRINGBUFFER_WITH_EMPTY_STRING" category="PERFORMANCE" experimental="true" /> <BugPattern abbrev="BAS" type="BAS_BLOATED_ASSIGNMENT_SCOPE" category="PERFORMANCE" experimental="true" /> <BugPattern abbrev="SCII" type="SCII_SPOILED_CHILD_INTERFACE_IMPLEMENTATOR" category="STYLE" experimental="true" /> + <BugPattern abbrev="DWI" type="DWI_DELETING_WHILE_ITERATING" category="CORRECTNESS" experimental="true" /> </FindbugsPlugin> \ No newline at end of file Modified: trunk/fb-contrib/etc/messages.xml =================================================================== --- trunk/fb-contrib/etc/messages.xml 2006-12-17 07:53:07 UTC (rev 736) +++ trunk/fb-contrib/etc/messages.xml 2006-12-19 06:57:27 UTC (rev 737) @@ -712,6 +712,17 @@ ]]> </Details> </Detector> + + <Detector class="com.mebigfatguy.fbcontrib.detect.DeletingWhileIterating"> + <Details> + <![CDATA[ + <p>looks for deletion of items from a collection using the remove method + of the collection at the same time that the collection is being iterated on. If + this occurs the iterator will become invalid and throw a ConcurrentModificationException. + Instead, the remove should be called on the iterator itself.</p> + ]]> + </Details> + </Detector> <!-- BugPattern --> @@ -1635,7 +1646,10 @@ <![CDATA[ <p>This method assigns a value to a variable in an outer scope compared to where the variable is actually used. Assuming this evaluation does not have side effects, the assignment can be moved into the inner scope (if block) - so that its execution time isn't taken up if the if guard is false.</p> + so that its execution time isn't taken up if the if guard is false. Care should be + taken however that the right hand side of the assignment does not contain side + effects that are required to happen, or that changes are not made further down that + will effect the execution of the assignment when done later on.</p> ]]> </Details> </BugPattern> @@ -1654,6 +1668,20 @@ </Details> </BugPattern> + <BugPattern type="DWI_DELETING_WHILE_ITERATING"> + <ShortDescription>Method deletes collection element while iterating</ShortDescription> + <LongDescription>Method {1} deletes collection element while iterating</LongDescription> + <Details> + <![CDATA[ + <p>This method removes items from a collection using the remove method of the collection, while + at the same time iterating across the collection. Doing this will invalidate the iterator, and further + user of it, will cause ConcurrentModificationExceptions to be thrown. To avoid this, the remove + method of the iterator should be used. + </p> + ]]> + </Details> + </BugPattern> + <!-- BugCode --> <BugCode abbrev="ISB">Inefficient String Buffering</BugCode> @@ -1715,4 +1743,5 @@ <BugCode abbrev="SPP">Sillyness Pot Pourri</BugCode> <BugCode abbrev="BAS">Bloated Assignment Scope</BugCode> <BugCode abbrev="SCII">Spoiled Child Interface Implementor</BugCode> + <BugCode abbrev="DWI">Deleting While Iterating</BugCode> </MessageCollection> \ No newline at end of file Added: trunk/fb-contrib/samples/DWI_Sample.java =================================================================== --- trunk/fb-contrib/samples/DWI_Sample.java (rev 0) +++ trunk/fb-contrib/samples/DWI_Sample.java 2006-12-19 06:57:27 UTC (rev 737) @@ -0,0 +1,16 @@ +import java.util.Iterator; +import java.util.Set; + +public class DWI_Sample +{ + public void deleteOdds(Set<Integer> bagOInts) + { + Iterator<Integer> it = bagOInts.iterator(); + while (it.hasNext()) + { + Integer i = it.next(); + if ((i.intValue() & 0x01) == 1) + bagOInts.remove(i); + } + } +} Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java (rev 0) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java 2006-12-19 06:57:27 UTC (rev 737) @@ -0,0 +1,216 @@ +/* + * fb-contrib - Auxilliary detectors for Java programs + * Copyright (C) 2005-2006 Dave Brosius + * + * 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 com.mebigfatguy.fbcontrib.detect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.BugReporter; +import edu.umd.cs.findbugs.BytecodeScanningDetector; +import edu.umd.cs.findbugs.OpcodeStack; +import edu.umd.cs.findbugs.ba.ClassContext; +import edu.umd.cs.findbugs.ba.XField; + +public class DeletingWhileIterating extends BytecodeScanningDetector +{ + private static JavaClass collectionClass; + private static JavaClass iteratorClass; + private static final Set<String> collectionMethods; + static { + try { + collectionClass = Repository.lookupClass("java.util.Collection"); + iteratorClass = Repository.lookupClass("java.util.Iterator"); + } catch (ClassNotFoundException cnfe) { + collectionClass = null; + iteratorClass = null; + } + + collectionMethods = new HashSet<String>(); + collectionMethods.add("entrySet()Ljava/lang/Set;"); + collectionMethods.add("keySet()Ljava/lang/Set;"); + collectionMethods.add("values()Ljava/lang/Collection;"); + } + + private BugReporter bugReporter; + private OpcodeStack stack; + private List<Set<Comparable>> collectionGroups; + private Map<Integer, Integer> iteratorToGroup; + + /** + * constructs a DWI detector given the reporter to report bugs on + * @param bugReporter the sync of bug reports + */ + public DeletingWhileIterating(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + /** + * implements the visitor to setup the opcode stack, and collectionGroups + * + * @param classContext the context object of the currently parsed class + */ + public void visitClassContext(ClassContext classContext) { + if ((collectionClass == null) || (iteratorClass == null)) + return; + + try { + stack = new OpcodeStack(); + collectionGroups = new ArrayList<Set<Comparable>>(); + iteratorToGroup = new HashMap<Integer, Integer>(); + super.visitClassContext(classContext); + } finally { + stack = null; + collectionGroups = null; + iteratorToGroup = null; + } + } + + /** + * implements the visitor to reset the stack and collectionGroups + * + * @param obj the context object of the currently parsed code block + */ + public void visitCode(Code obj) { + stack.resetForMethodEntry(this); + collectionGroups.clear(); + iteratorToGroup.clear(); + super.visitCode(obj); + } + + /** + * implements the visitor to look for deletes on collections that are being iterated + * + * @param seen the opcode of the currently parsed instruction + */ + public void sawOpcode(int seen) { + int groupId = -1; + + try { + if (seen == INVOKEINTERFACE) + { + String className = getClassConstantOperand(); + if (isCollection(className)) { + String methodName = getNameConstantOperand(); + String signature = getSigConstantOperand(); + String methodInfo = methodName + signature; + if (collectionMethods.contains(methodInfo)) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + groupId = findCollectionGroup(itm); + + } + } else if ("iterator()Ljava/util/Iterator;".equals(methodInfo)) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + groupId = findCollectionGroup(itm); + + } + } else if ("remove(Ljava/lang/Object;)Z".equals(methodInfo)) { + + } + } + } else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item itm = stack.getStackItem(0); + Integer id = (Integer)itm.getUserValue(); + if (id != null) { + int reg = RegisterUtils.getAStoreReg(this, seen); + + try { + if (itm.getJavaClass().implementationOf(iteratorClass)) { + iteratorToGroup.put(Integer14.valueOf(reg), id); + } else { + Set<Comparable> group = collectionGroups.get(id); + if (group != null) { + group.add(Integer14.valueOf(reg)); + } + } + } catch (ClassNotFoundException cnfe) { + bugReporter.reportMissingClass(cnfe); + } + } + } + + } + } finally { + stack.sawOpcode(this, seen); + if ((groupId >= 0) && (stack.getStackDepth() > 0)) { + OpcodeStack.Item itm = stack.getStackItem(0); + itm.setUserValue(Integer14.valueOf(groupId)); + } + + } + } + + private boolean isCollection(String className) { + try { + JavaClass cls = Repository.lookupClass(className); + return cls.implementationOf(collectionClass); + } catch (ClassNotFoundException cnfe) { + bugReporter.reportMissingClass(cnfe); + return false; + } + } + + private int findCollectionGroup(OpcodeStack.Item itm) { + Comparable groupElement = null; + + Integer id = (Integer)itm.getUserValue(); + if (id != null) + return id.intValue(); + + int reg = itm.getRegisterNumber(); + if (reg >= 0) + groupElement = Integer14.valueOf(reg); + else { + XField field = itm.getXField(); + if (field != null) { + groupElement = field.getName(); + } + } + + if (groupElement != null) { + int numGroups = collectionGroups.size(); + for (int i = 0; i < numGroups; i++) { + Set<? extends Comparable> group = collectionGroups.get(i); + if (group.contains(groupElement)) { + return i; + } + } + + Set<Comparable> group = new HashSet<Comparable>(); + group.add(groupElement); + collectionGroups.add(group); + return collectionGroups.size() - 1; + } + + return -1; + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |