[Fb-contrib-commit] SF.net SVN: fb-contrib: [898] trunk/fb-contrib/src/com/mebigfatguy/ fbcontrib/d
Brought to you by:
dbrosius
From: <dbr...@us...> - 2007-09-15 04:29:09
|
Revision: 898 http://fb-contrib.svn.sourceforge.net/fb-contrib/?rev=898&view=rev Author: dbrosius Date: 2007-09-14 21:29:10 -0700 (Fri, 14 Sep 2007) Log Message: ----------- initial checkin MRC detector Modified Paths: -------------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/InheritanceTypeChecking.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LostExceptionStackTrace.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NonOwnedSynchronization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NonRecycleableTaglibs.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossibleIncompleteSerialization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossiblyRedundantMethodCalls.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SQLInLoop.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/Section508Compliance.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SillynessPotPourri.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SpoiledChildInterfaceImplementor.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/StaticArrayCreatedInMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SuspiciousComparatorReturnValues.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SuspiciousJDKVersionUse.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/TailRecursion.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UnrelatedReturnValues.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseCharacterParameterizedMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseEnumCollections.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseSplit.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseToArray.java Added Paths: ----------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/MethodReturnsConstant.java Property Changed: ---------------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbnormalFinallyBlockReturn.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbstractClassEmptyMethods.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbstractOverriddenMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ArrayBasedCollections.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ArrayWrappedCallByReference.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedSynchronizedBlock.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ClassEnvy.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConfusingAutoboxedOverloading.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConstantListIndex.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CopiedOverriddenMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CustomBuiltXML.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CyclomaticComplexity.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DateComparison.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeclaredRuntimeException.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DubiousListCollection.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/FieldCouldBeLocal.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/FinalParameters.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/FloatingPointLoops.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/InefficientStringBuffering.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/InheritanceTypeChecking.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/JDBCVendorReliance.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ListIndexedIterating.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LiteralStringComparison.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LocalSynchronizedCollection.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LostExceptionStackTrace.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ManualArrayCopy.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessAutoboxing.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessInstanceRetrieval.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NonCollectionMethodUse.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NonOwnedSynchronization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NonRecycleableTaglibs.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/OrphanedDOMNode.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/OverlyConcreteParameter.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ParallelLists.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PartiallyConstructedObjectAccess.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossibleIncompleteSerialization.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossibleMemoryBloat.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossiblyRedundantMethodCalls.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SQLInLoop.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/Section508Compliance.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SillynessPotPourri.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SloppyClassReflection.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SluggishGui.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SpoiledChildInterfaceImplementor.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SpuriousThreadStates.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/StaticArrayCreatedInMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/StaticMethodInstanceInvocation.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SuspiciousComparatorReturnValues.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SuspiciousJDKVersionUse.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SuspiciousWaitOnConcurrentObject.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SyncCollectionIterators.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/TailRecursion.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UnnecessaryStoreBeforeReturn.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UnrelatedCollectionContents.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UnrelatedReturnValues.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseAddAll.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseCharacterParameterizedMethod.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseEnumCollections.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseSplit.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UseToArray.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/package.html Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbnormalFinallyBlockReturn.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbstractClassEmptyMethods.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/AbstractOverriddenMethod.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ArrayBasedCollections.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ArrayWrappedCallByReference.java ___________________________________________________________________ Name: svn:mime-type + text/plain Modified: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java 2007-09-15 03:02:05 UTC (rev 897) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java 2007-09-15 04:29:10 UTC (rev 898) @@ -1,578 +1,578 @@ -/* - * fb-contrib - Auxilliary detectors for Java programs - * Copyright (C) 2005-2007 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.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.bcel.classfile.Code; -import org.apache.bcel.classfile.CodeException; -import org.apache.bcel.classfile.Method; - -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.ba.ClassContext; - -/** - * looks for variable assignments at a scope larger than its use. In this case, - * the assignment can be pushed down into the smaller scope to reduce the performance - * impact of that assignment. - */ -public class BloatedAssignmentScope extends BytecodeScanningDetector -{ - BugReporter bugReporter; - private Set<Integer> ignoreRegs; - private ScopeBlock rootScopeBlock; - private Set<Integer> catchHandlers; - private Set<Integer> switchTargets; - private boolean dontReport; - private boolean sawDup; - private boolean sawNull; - - /** - * constructs a BAS detector given the reporter to report bugs on - - * @param bugReporter the sync of bug reports - */ - public BloatedAssignmentScope(BugReporter bugReporter) { - this.bugReporter = bugReporter; - } - - /** - * implements the visitor to create and the clear the register to location map - * - * @param classContext the context object of the currently parsed class - */ - @Override - public void visitClassContext(ClassContext classContext) { - try { - ignoreRegs = new HashSet<Integer>(); - catchHandlers = new HashSet<Integer>(); - switchTargets = new HashSet<Integer>(); - super.visitClassContext(classContext); - } finally { - ignoreRegs = null; - catchHandlers = null; - switchTargets = null; - } - } - - /** - * implements the visitor to reset the register to location map - * - * @param obj the context object of the currently parsed code block - */ - @Override - public void visitCode(Code obj) { - try { - - ignoreRegs.clear(); - Method method = getMethod(); - if (!method.isStatic()) - ignoreRegs.add(Integer14.valueOf(0)); - - int[] parmRegs = RegisterUtils.getParameterRegisters(method); - for (int parm : parmRegs) { - ignoreRegs.add(Integer14.valueOf(parm)); - } - - rootScopeBlock = new ScopeBlock(0, obj.getLength()); - catchHandlers.clear(); - CodeException[] exceptions = obj.getExceptionTable(); - if (exceptions != null) { - for (CodeException ex : exceptions) { - catchHandlers.add(Integer14.valueOf(ex.getHandlerPC())); - } - } - - switchTargets.clear(); - dontReport = false; - sawDup = false; - sawNull = false; - super.visitCode(obj); - - if (!dontReport) - rootScopeBlock.findBugs(new HashSet<Integer>()); - - } finally { - rootScopeBlock = null; - } - } - - /** - * implements the visitor to look for variables assigned below the scope - * in which they are used. - * - * @param seen the opcode of the currently parsed instruction - */ - @Override - public void sawOpcode(int seen) { - if ((seen == ASTORE) - || (seen == ISTORE) - || (seen == LSTORE) - || (seen == FSTORE) - || (seen == DSTORE) - || ((seen >= ASTORE_0) && (seen <= ASTORE_3)) - || ((seen >= ISTORE_0) && (seen <= ISTORE_3)) - || ((seen >= LSTORE_0) && (seen <= LSTORE_1)) - || ((seen >= FSTORE_0) && (seen <= FSTORE_1)) - || ((seen >= DSTORE_0) && (seen <= DSTORE_1))) { - int reg = RegisterUtils.getStoreReg(this, seen); - Integer iReg = Integer14.valueOf(reg); - int pc = getPC(); - if (catchHandlers.contains(Integer14.valueOf(pc))) - ignoreRegs.add(iReg); - else if (sawNull) - ignoreRegs.add(iReg); - - if (!ignoreRegs.contains(iReg)) { - ScopeBlock sb = findScopeBlock(rootScopeBlock, pc); - if (sb != null) { - sb.addStore(reg, pc); - if (sawDup) - sb.addLoad(reg, pc); - } - else - ignoreRegs.add(iReg); - } - } else if ((seen == ALOAD) - || (seen == ILOAD) - || (seen == LLOAD) - || (seen == FLOAD) - || (seen == DLOAD) - || ((seen >= ALOAD_0) && (seen <= ALOAD_3)) - || ((seen >= ILOAD_0) && (seen <= ILOAD_3)) - || ((seen >= LLOAD_0) && (seen <= LLOAD_1)) - || ((seen >= FLOAD_0) && (seen <= FLOAD_1)) - || ((seen >= DLOAD_0) && (seen <= DLOAD_1))) { - int reg = RegisterUtils.getLoadReg(this, seen); - if (!ignoreRegs.contains(Integer14.valueOf(reg))) { - ScopeBlock sb = findScopeBlock(rootScopeBlock, getPC()); - if (sb != null) - sb.addLoad(reg, getPC()); - else - ignoreRegs.add(Integer14.valueOf(reg)); - } - } else if (((seen >= IFEQ) && (seen <= GOTO)) || (seen == GOTO_W)) { - int target = getBranchTarget(); - if (target > getPC()) { - if ((seen == GOTO) || (seen == GOTO_W)) { - Integer nextPC = Integer14.valueOf(getNextPC()); - if (!switchTargets.contains(nextPC)) { - ScopeBlock sb = findScopeBlockWithTarget(rootScopeBlock, getPC(), getNextPC()); - if (sb == null) { - sb = new ScopeBlock(getPC(), target); - sb.setLoop(); - sb.setGoto(); - rootScopeBlock.addChild(sb); - } else { - sb = new ScopeBlock(getPC(), target); - sb.setGoto(); - rootScopeBlock.addChild(sb); - } - } - } else { - ScopeBlock sb = findScopeBlockWithTarget(rootScopeBlock, getPC(), target); - if ((sb != null) && (!sb.isLoop) && !sb.hasChildren()) { - if (sb.isGoto()) { - ScopeBlock parent = sb.getParent(); - sb.pushUpLoadStores(); - if (parent != null) - parent.removeChild(sb); - sb = new ScopeBlock(getPC(), target); - rootScopeBlock.addChild(sb); - } else { - sb.pushUpLoadStores(); - sb.setStart(getPC()); - sb.setFinish(target); - } - } else { - sb = new ScopeBlock(getPC(), target); - rootScopeBlock.addChild(sb); - } - } - } else { - ScopeBlock sb = findScopeBlock(rootScopeBlock, getPC()); - if (sb != null) - sb.setLoop(); - } - } else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) { - int pc = getPC(); - int[] offsets = getSwitchOffsets(); - List<Integer> targets = new ArrayList<Integer>(); - for (int i = 0; i < offsets.length; i++) - targets.add(Integer14.valueOf(offsets[i] + pc)); - Integer defOffset = Integer14.valueOf(getDefaultSwitchOffset() + pc); - if (!targets.contains(defOffset)) - targets.add(defOffset); - Collections.sort(targets); - - Integer lastTarget = targets.get(0); - for (int i = 1; i < targets.size(); i++) { - Integer nextTarget = targets.get(i); - ScopeBlock sb = new ScopeBlock(lastTarget.intValue(), nextTarget.intValue()); - rootScopeBlock.addChild(sb); - lastTarget = nextTarget; - } - switchTargets.addAll(targets); - } else if ((seen == INVOKEVIRTUAL) || (seen == INVOKEVIRTUAL)) { - if ("wasNull".equals(getNameConstantOperand()) - && "()Z".equals(getSigConstantOperand())) - dontReport = true; - } - - sawDup = (seen == DUP); - sawNull = (seen == ACONST_NULL); - } - - /** - * returns the scope block in which this register was assigned, by traversing the scope block tree - * - * @param sb the scope block to start searching in - * @param pc the current program counter - * @return the scope block or null if not found - */ - private ScopeBlock findScopeBlock(ScopeBlock sb, int pc) { - - if ((pc > sb.getStart()) && (pc < sb.getFinish())) { - if (sb.children != null) { - for (ScopeBlock child : sb.children) { - ScopeBlock foundSb = findScopeBlock(child, pc); - if (foundSb != null) - return foundSb; - } - } - return sb; - } - return null; - } - - /** - * returns an existing scope block that has the same target as the one looked for - * - * @param sb the scope block to start with - * @param target the target to look for - * - * @return the scope block found or null - */ - private ScopeBlock findScopeBlockWithTarget(ScopeBlock sb, int start, int target) { - ScopeBlock parentBlock = null; - if ((sb.startLocation < start) && (sb.finishLocation >= start)) { - if ((sb.finishLocation <= target) || (sb.isGoto() && !sb.isLoop())) - parentBlock = sb; - } - - if (sb.children != null) - { - for (ScopeBlock child : sb.children) { - ScopeBlock targetBlock = findScopeBlockWithTarget(child, start, target); - if (targetBlock != null) - return targetBlock; - } - } - - return parentBlock; - } - - /** holds the description of a scope { } block, be it a for, if, while block - */ - private class ScopeBlock - { - private ScopeBlock parent; - private int startLocation; - private int finishLocation; - private boolean isLoop; - private boolean isGoto; - private Map<Integer, Integer> loads; - private Map<Integer, Integer> stores; - private List<ScopeBlock> children; - - /** construts a new scope block - * - * @param start the beginning of the block - * @param finish the end of the block - */ - public ScopeBlock(int start, int finish) { - parent = null; - startLocation = start; - finishLocation = finish; - isLoop = false; - isGoto = false; - loads = null; - stores = null; - children = null; - } - - /** - * returns a string representation of the scope block - * - * @returns a string representation - */ - @Override - public String toString() { - return "Start=" + startLocation + " Finish=" + finishLocation + " Loop=" + isLoop + " Loads=" + loads + " Stores=" + stores; - } - - /** - * returns the scope blocks parent - * - * @return the parent of this scope block - */ - public ScopeBlock getParent() { - return parent; - } - - /** returns the start of the block - * - * @return the start of the block - */ - public int getStart() { - return startLocation; - } - - /** returns the end of the block - * - * @return the end of the block - */ - public int getFinish() { - return finishLocation; - } - - /** - * sets the start pc of the block - * @param start the start pc - */ - public void setStart(int start) { - startLocation = start; - } - - /** - * sets the finish pc of the block - * @param finish the finish pc - */ - public void setFinish(int finish) { - finishLocation = finish; - } - - public boolean hasChildren() { - return children != null; - } - /** - * sets that this block is a loop - */ - public void setLoop() { - isLoop = true; - } - - /** - * returns whether this scope block is a loop - * - * @returns whether this block is a loop - */ - public boolean isLoop() { - return isLoop; - } - - /** - * sets that this block was caused from a goto, (an if block exit) - */ - public void setGoto() { - isGoto = true; - } - - /** - * returns whether this block was caused from a goto - * - * @returns whether this block was caused by a goto - */ - public boolean isGoto() { - return isGoto; - } - - /** - * adds the register as a store in this scope block - * - * @param reg the register that was stored - * @param pc the instruction that did the store - */ - public void addStore(int reg, int pc) { - if (stores == null) - stores = new HashMap<Integer, Integer>(); - - stores.put(Integer14.valueOf(reg), Integer14.valueOf(pc)); - } - - /** - * adds the register as a load in this scope block - * - * @param reg the register that was loaded - * @param pc the instruction that did the load - */ - public void addLoad(int reg, int pc) { - if (loads == null) - loads = new HashMap<Integer, Integer>(); - - loads.put(Integer14.valueOf(reg), Integer14.valueOf(pc)); - } - - /** - * adds a scope block to this subtree by finding the correct place in the hierarchy to store it - * - * @param child the scope block to add to the tree - */ - public void addChild(ScopeBlock newChild) { - newChild.parent = this; - - if (children != null) { - for (ScopeBlock child : children) { - if ((newChild.startLocation > child.startLocation) && (newChild.finishLocation < child.finishLocation)) { - child.addChild(newChild); - return; - } - } - int pos = 0; - for (ScopeBlock child : children) { - if (newChild.startLocation < child.startLocation) { - children.add(pos, newChild); - return; - } - pos++; - } - children.add(newChild); - return; - } - children = new ArrayList<ScopeBlock>(); - children.add(newChild); - } - - /** - * removes a child from this node - * @param child the child to remove - */ - public void removeChild(ScopeBlock child) { - if (children != null) - children.remove(child); - } - - /** - * report stores that occur at scopes higher than associated loads that are not involved with loops - */ - public void findBugs(Set<Integer> parentUsedRegs) { - if (isLoop) - return; - - Set<Integer> usedRegs = new HashSet<Integer>(parentUsedRegs); - if (stores != null) - usedRegs.addAll(stores.keySet()); - if (loads != null) - usedRegs.addAll(loads.keySet()); - - if (stores != null) { - if (loads != null) - stores.keySet().removeAll(loads.keySet()); - stores.keySet().removeAll(parentUsedRegs); - - if (stores.size() > 0) { - if (children != null) { - for (Map.Entry<Integer, Integer> entry : stores.entrySet()) { - int childUseCount = 0; - boolean inLoop = false; - Integer reg = entry.getKey(); - for (ScopeBlock child : children) { - if (child.usesReg(reg)) { - if (child.isLoop) { - inLoop = true; - break; - } - childUseCount++; - } - } - if ((!inLoop) && (childUseCount == 1)) { - bugReporter.reportBug(new BugInstance(BloatedAssignmentScope.this, "BAS_BLOATED_ASSIGNMENT_SCOPE", NORMAL_PRIORITY) - .addClass(BloatedAssignmentScope.this) - .addMethod(BloatedAssignmentScope.this) - .addSourceLine(BloatedAssignmentScope.this, entry.getValue().intValue())); - } - } - } - } - } - - if (children != null) { - for (ScopeBlock child : children) { - child.findBugs(usedRegs); - } - } - } - - /** - * returns whether this block either loads or stores into the register in question - * - * @param reg the register to look for loads or stores - * - * @return whether the block uses the register - */ - public boolean usesReg(Integer reg) { - if ((loads != null) && (loads.containsKey(reg))) - return true; - if ((stores != null) && (stores.containsKey(reg))) - return true; - - if (children != null) { - for (ScopeBlock child : children) { - if (child.usesReg(reg)) - return true; - } - } - - return false; - } - - /** - * push all loads and stores to this block up to the parent - */ - public void pushUpLoadStores() { - if (parent != null) { - if (loads != null) { - if (parent.loads != null) - parent.loads.putAll(loads); - else - parent.loads = loads; - } - if (stores != null) { - if (parent.stores != null) - parent.stores.putAll(stores); - else - parent.stores = stores; - } - loads = null; - stores = null; - } - } - } -} +/* + * fb-contrib - Auxilliary detectors for Java programs + * Copyright (C) 2005-2007 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.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.CodeException; +import org.apache.bcel.classfile.Method; + +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.ba.ClassContext; + +/** + * looks for variable assignments at a scope larger than its use. In this case, + * the assignment can be pushed down into the smaller scope to reduce the performance + * impact of that assignment. + */ +public class BloatedAssignmentScope extends BytecodeScanningDetector +{ + BugReporter bugReporter; + private Set<Integer> ignoreRegs; + private ScopeBlock rootScopeBlock; + private Set<Integer> catchHandlers; + private Set<Integer> switchTargets; + private boolean dontReport; + private boolean sawDup; + private boolean sawNull; + + /** + * constructs a BAS detector given the reporter to report bugs on + + * @param bugReporter the sync of bug reports + */ + public BloatedAssignmentScope(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + /** + * implements the visitor to create and the clear the register to location map + * + * @param classContext the context object of the currently parsed class + */ + @Override + public void visitClassContext(ClassContext classContext) { + try { + ignoreRegs = new HashSet<Integer>(); + catchHandlers = new HashSet<Integer>(); + switchTargets = new HashSet<Integer>(); + super.visitClassContext(classContext); + } finally { + ignoreRegs = null; + catchHandlers = null; + switchTargets = null; + } + } + + /** + * implements the visitor to reset the register to location map + * + * @param obj the context object of the currently parsed code block + */ + @Override + public void visitCode(Code obj) { + try { + + ignoreRegs.clear(); + Method method = getMethod(); + if (!method.isStatic()) + ignoreRegs.add(Integer14.valueOf(0)); + + int[] parmRegs = RegisterUtils.getParameterRegisters(method); + for (int parm : parmRegs) { + ignoreRegs.add(Integer14.valueOf(parm)); + } + + rootScopeBlock = new ScopeBlock(0, obj.getLength()); + catchHandlers.clear(); + CodeException[] exceptions = obj.getExceptionTable(); + if (exceptions != null) { + for (CodeException ex : exceptions) { + catchHandlers.add(Integer14.valueOf(ex.getHandlerPC())); + } + } + + switchTargets.clear(); + dontReport = false; + sawDup = false; + sawNull = false; + super.visitCode(obj); + + if (!dontReport) + rootScopeBlock.findBugs(new HashSet<Integer>()); + + } finally { + rootScopeBlock = null; + } + } + + /** + * implements the visitor to look for variables assigned below the scope + * in which they are used. + * + * @param seen the opcode of the currently parsed instruction + */ + @Override + public void sawOpcode(int seen) { + if ((seen == ASTORE) + || (seen == ISTORE) + || (seen == LSTORE) + || (seen == FSTORE) + || (seen == DSTORE) + || ((seen >= ASTORE_0) && (seen <= ASTORE_3)) + || ((seen >= ISTORE_0) && (seen <= ISTORE_3)) + || ((seen >= LSTORE_0) && (seen <= LSTORE_1)) + || ((seen >= FSTORE_0) && (seen <= FSTORE_1)) + || ((seen >= DSTORE_0) && (seen <= DSTORE_1))) { + int reg = RegisterUtils.getStoreReg(this, seen); + Integer iReg = Integer14.valueOf(reg); + int pc = getPC(); + if (catchHandlers.contains(Integer14.valueOf(pc))) + ignoreRegs.add(iReg); + else if (sawNull) + ignoreRegs.add(iReg); + + if (!ignoreRegs.contains(iReg)) { + ScopeBlock sb = findScopeBlock(rootScopeBlock, pc); + if (sb != null) { + sb.addStore(reg, pc); + if (sawDup) + sb.addLoad(reg, pc); + } + else + ignoreRegs.add(iReg); + } + } else if ((seen == ALOAD) + || (seen == ILOAD) + || (seen == LLOAD) + || (seen == FLOAD) + || (seen == DLOAD) + || ((seen >= ALOAD_0) && (seen <= ALOAD_3)) + || ((seen >= ILOAD_0) && (seen <= ILOAD_3)) + || ((seen >= LLOAD_0) && (seen <= LLOAD_1)) + || ((seen >= FLOAD_0) && (seen <= FLOAD_1)) + || ((seen >= DLOAD_0) && (seen <= DLOAD_1))) { + int reg = RegisterUtils.getLoadReg(this, seen); + if (!ignoreRegs.contains(Integer14.valueOf(reg))) { + ScopeBlock sb = findScopeBlock(rootScopeBlock, getPC()); + if (sb != null) + sb.addLoad(reg, getPC()); + else + ignoreRegs.add(Integer14.valueOf(reg)); + } + } else if (((seen >= IFEQ) && (seen <= GOTO)) || (seen == GOTO_W)) { + int target = getBranchTarget(); + if (target > getPC()) { + if ((seen == GOTO) || (seen == GOTO_W)) { + Integer nextPC = Integer14.valueOf(getNextPC()); + if (!switchTargets.contains(nextPC)) { + ScopeBlock sb = findScopeBlockWithTarget(rootScopeBlock, getPC(), getNextPC()); + if (sb == null) { + sb = new ScopeBlock(getPC(), target); + sb.setLoop(); + sb.setGoto(); + rootScopeBlock.addChild(sb); + } else { + sb = new ScopeBlock(getPC(), target); + sb.setGoto(); + rootScopeBlock.addChild(sb); + } + } + } else { + ScopeBlock sb = findScopeBlockWithTarget(rootScopeBlock, getPC(), target); + if ((sb != null) && (!sb.isLoop) && !sb.hasChildren()) { + if (sb.isGoto()) { + ScopeBlock parent = sb.getParent(); + sb.pushUpLoadStores(); + if (parent != null) + parent.removeChild(sb); + sb = new ScopeBlock(getPC(), target); + rootScopeBlock.addChild(sb); + } else { + sb.pushUpLoadStores(); + sb.setStart(getPC()); + sb.setFinish(target); + } + } else { + sb = new ScopeBlock(getPC(), target); + rootScopeBlock.addChild(sb); + } + } + } else { + ScopeBlock sb = findScopeBlock(rootScopeBlock, getPC()); + if (sb != null) + sb.setLoop(); + } + } else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) { + int pc = getPC(); + int[] offsets = getSwitchOffsets(); + List<Integer> targets = new ArrayList<Integer>(); + for (int i = 0; i < offsets.length; i++) + targets.add(Integer14.valueOf(offsets[i] + pc)); + Integer defOffset = Integer14.valueOf(getDefaultSwitchOffset() + pc); + if (!targets.contains(defOffset)) + targets.add(defOffset); + Collections.sort(targets); + + Integer lastTarget = targets.get(0); + for (int i = 1; i < targets.size(); i++) { + Integer nextTarget = targets.get(i); + ScopeBlock sb = new ScopeBlock(lastTarget.intValue(), nextTarget.intValue()); + rootScopeBlock.addChild(sb); + lastTarget = nextTarget; + } + switchTargets.addAll(targets); + } else if ((seen == INVOKEVIRTUAL) || (seen == INVOKEVIRTUAL)) { + if ("wasNull".equals(getNameConstantOperand()) + && "()Z".equals(getSigConstantOperand())) + dontReport = true; + } + + sawDup = (seen == DUP); + sawNull = (seen == ACONST_NULL); + } + + /** + * returns the scope block in which this register was assigned, by traversing the scope block tree + * + * @param sb the scope block to start searching in + * @param pc the current program counter + * @return the scope block or null if not found + */ + private ScopeBlock findScopeBlock(ScopeBlock sb, int pc) { + + if ((pc > sb.getStart()) && (pc < sb.getFinish())) { + if (sb.children != null) { + for (ScopeBlock child : sb.children) { + ScopeBlock foundSb = findScopeBlock(child, pc); + if (foundSb != null) + return foundSb; + } + } + return sb; + } + return null; + } + + /** + * returns an existing scope block that has the same target as the one looked for + * + * @param sb the scope block to start with + * @param target the target to look for + * + * @return the scope block found or null + */ + private ScopeBlock findScopeBlockWithTarget(ScopeBlock sb, int start, int target) { + ScopeBlock parentBlock = null; + if ((sb.startLocation < start) && (sb.finishLocation >= start)) { + if ((sb.finishLocation <= target) || (sb.isGoto() && !sb.isLoop())) + parentBlock = sb; + } + + if (sb.children != null) + { + for (ScopeBlock child : sb.children) { + ScopeBlock targetBlock = findScopeBlockWithTarget(child, start, target); + if (targetBlock != null) + return targetBlock; + } + } + + return parentBlock; + } + + /** holds the description of a scope { } block, be it a for, if, while block + */ + private class ScopeBlock + { + private ScopeBlock parent; + private int startLocation; + private int finishLocation; + private boolean isLoop; + private boolean isGoto; + private Map<Integer, Integer> loads; + private Map<Integer, Integer> stores; + private List<ScopeBlock> children; + + /** construts a new scope block + * + * @param start the beginning of the block + * @param finish the end of the block + */ + public ScopeBlock(int start, int finish) { + parent = null; + startLocation = start; + finishLocation = finish; + isLoop = false; + isGoto = false; + loads = null; + stores = null; + children = null; + } + + /** + * returns a string representation of the scope block + * + * @returns a string representation + */ + @Override + public String toString() { + return "Start=" + startLocation + " Finish=" + finishLocation + " Loop=" + isLoop + " Loads=" + loads + " Stores=" + stores; + } + + /** + * returns the scope blocks parent + * + * @return the parent of this scope block + */ + public ScopeBlock getParent() { + return parent; + } + + /** returns the start of the block + * + * @return the start of the block + */ + public int getStart() { + return startLocation; + } + + /** returns the end of the block + * + * @return the end of the block + */ + public int getFinish() { + return finishLocation; + } + + /** + * sets the start pc of the block + * @param start the start pc + */ + public void setStart(int start) { + startLocation = start; + } + + /** + * sets the finish pc of the block + * @param finish the finish pc + */ + public void setFinish(int finish) { + finishLocation = finish; + } + + public boolean hasChildren() { + return children != null; + } + /** + * sets that this block is a loop + */ + public void setLoop() { + isLoop = true; + } + + /** + * returns whether this scope block is a loop + * + * @returns whether this block is a loop + */ + public boolean isLoop() { + return isLoop; + } + + /** + * sets that this block was caused from a goto, (an if block exit) + */ + public void setGoto() { + isGoto = true; + } + + /** + * returns whether this block was caused from a goto + * + * @returns whether this block was caused by a goto + */ + public boolean isGoto() { + return isGoto; + } + + /** + * adds the register as a store in this scope block + * + * @param reg the register that was stored + * @param pc the instruction that did the store + */ + public void addStore(int reg, int pc) { + if (stores == null) + stores = new HashMap<Integer, Integer>(); + + stores.put(Integer14.valueOf(reg), Integer14.valueOf(pc)); + } + + /** + * adds the register as a load in this scope block + * + * @param reg the register that was loaded + * @param pc the instruction that did the load + */ + public void addLoad(int reg, int pc) { + if (loads == null) + loads = new HashMap<Integer, Integer>(); + + loads.put(Integer14.valueOf(reg), Integer14.valueOf(pc)); + } + + /** + * adds a scope block to this subtree by finding the correct place in the hierarchy to store it + * + * @param child the scope block to add to the tree + */ + public void addChild(ScopeBlock newChild) { + newChild.parent = this; + + if (children != null) { + for (ScopeBlock child : children) { + if ((newChild.startLocation > child.startLocation) && (newChild.finishLocation < child.finishLocation)) { + child.addChild(newChild); + return; + } + } + int pos = 0; + for (ScopeBlock child : children) { + if (newChild.startLocation < child.startLocation) { + children.add(pos, newChild); + return; + } + pos++; + } + children.add(newChild); + return; + } + children = new ArrayList<ScopeBlock>(); + children.add(newChild); + } + + /** + * removes a child from this node + * @param child the child to remove + */ + public void removeChild(ScopeBlock child) { + if (children != null) + children.remove(child); + } + + /** + * report stores that occur at scopes higher than associated loads that are not involved with loops + */ + public void findBugs(Set<Integer> parentUsedRegs) { + if (isLoop) + return; + + Set<Integer> usedRegs = new HashSet<Integer>(parentUsedRegs); + if (stores != null) + usedRegs.addAll(stores.keySet()); + if (loads != null) + usedRegs.addAll(loads.keySet()); + + if (stores != null) { + if (loads != null) + stores.keySet().removeAll(loads.keySet()); + stores.keySet().removeAll(parentUsedRegs); + + if (stores.size() > 0) { + if (children != null) { + for (Map.Entry<Integer, Integer> entry : stores.entrySet()) { + int childUseCount = 0; + boolean inLoop = false; + Integer reg = entry.getKey(); + for (ScopeBlock child : children) { + if (child.usesReg(reg)) { + if (child.isLoop) { + inLoop = true; + break; + } + childUseCount++; + } + } + if ((!inLoop) && (childUseCount == 1)) { + bugReporter.reportBug(new BugInstance(BloatedAssignmentScope.this, "BAS_BLOATED_ASSIGNMENT_SCOPE", NORMAL_PRIORITY) + .addClass(BloatedAssignmentScope.this) + .addMethod(BloatedAssignmentScope.this) + .addSourceLine(BloatedAssignmentScope.this, entry.getValue().intValue())); + } + } + } + } + } + + if (children != null) { + for (ScopeBlock child : children) { + child.findBugs(usedRegs); + } + } + } + + /** + * returns whether this block either loads or stores into the register in question + * + * @param reg the register to look for loads or stores + * + * @return whether the block uses the register + */ + public boolean usesReg(Integer reg) { + if ((loads != null) && (loads.containsKey(reg))) + return true; + if ((stores != null) && (stores.containsKey(reg))) + return true; + + if (children != null) { + for (ScopeBlock child : children) { + if (child.usesReg(reg)) + return true; + } + } + + return false; + } + + /** + * push all loads and stores to this block up to the parent + */ + public void pushUpLoadStores() { + if (parent != null) { + if (loads != null) { + if (parent.loads != null) + parent.loads.putAll(loads); + else + parent.loads = loads; + } + if (stores != null) { + if (parent.stores != null) + parent.stores.putAll(stores); + else + parent.stores = stores; + } + loads = null; + stores = null; + } + } + } +} Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedAssignmentScope.java ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:eol-style + native Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/BloatedSynchronizedBlock.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ClassEnvy.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConfusingAutoboxedOverloading.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConstantListIndex.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CopiedOverriddenMethod.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CustomBuiltXML.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/CyclomaticComplexity.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DateComparison.java ___________________________________________________________________ Name: svn:mime-type + text/plain Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeclaredRuntimeException.java ___________________________________________________________________ Name: svn:mime-type + text/plain Modified: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java 2007-09-15 03:02:05 UTC (rev 897) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/DeletingWhileIterating.java 2007-09-15 04:29:10 UTC (rev 898) @@ -1,378 +1,378 @@ -/* - * fb-contrib - Auxiliary detectors for Java programs - * Copyright (C) 2005-2007 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.Iterator; -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.BugInstance; -import edu.umd.cs.findbugs.BugReporter; -import edu.umd.cs.findbugs.BytecodeScanningDetector; -import edu.umd.cs.findbugs.FieldAnnotation; -import edu.umd.cs.findbugs.OpcodeStack; -import edu.umd.cs.findbugs.ba.ClassContext; -import edu.umd.cs.findbugs.ba.XField; -import edu.umd.cs.findbugs.classfile.FieldDescriptor; - -/** - * 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. - */ -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 static final Map<String, Integer> modifyingMethods = new HashMap<String, Integer>(); - static { - modifyingMethods.put("add(Ljava/lang/Object;)Z", Integer14.valueOf(1)); - modifyingMethods.put("addAll(Ljava/util/Collection;)Z", Integer14.valueOf(1)); - modifyingMethods.put("addAll(ILjava/util/Collection;)Z", Integer14.valueOf(2)); - modifyingMethods.put("clear()V", Integer14.valueOf(0)); - modifyingMethods.put("remove(I)Ljava/lang/Object;", Integer14.valueOf(1)); - modifyingMethods.put("removeAll(Ljava/util/Collection;)Z", Integer14.valueOf(1)); - modifyingMethods.put("retainAll(Ljava/util/Collection;)Z", Integer14.valueOf(1)); - modifyingMethods.put("set(ILjava/lang/Object;)Ljava/lang/Object;", Integer14.valueOf(2)); - - } - - private BugReporter bugReporter; - private OpcodeStack stack; - private List<Set<Comparable>> collectionGroups; - private Map<Integer, Integer> groupToIterator; - private Map<Integer, Loop> loops; - - /** - * 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, collectionGroups, groupToIterator and loops - * - * @param classContext the context object of the currently parsed class - */ - @Override - public void visitClassContext(ClassContext classContext) { - if ((collectionClass == null) || (iteratorClass == null)) - return; - - try { - stack = new OpcodeStack(); - collectionGroups = new ArrayList<Set<Comparable>>(); - groupToIterator = new HashMap<Integer, Integer>(); - loops = new HashMap<Integer, Loop>(); - super.visitClassContext(classContext); - } finally { - stack = null; - collectionGroups = null; - groupToIterator = null; - loops = null; - } - } - - /** - * implements the visitor to reset the stack, collectionGroups, groupToIterator and loops - * - * @param obj the context object of the currently parsed code block - */ - @Override - public void visitCode(Code obj) { - stack.resetForMethodEntry(this); - collectionGroups.clear(); - groupToIterator.clear(); - loops.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 - */ - @Override - public void sawOpcode(int seen) { - int groupId = -1; - - try { - if (seen == INVOKEINTERFACE) - { - String className = getClassConstantOperand(); - String methodName = getNameConstantOperand(); - String signature = getSigConstantOperand(); - String methodInfo = methodName + signature; - - if (isCollection(className)) { - if (collectionMethods.contains(methodInfo)) { - if (stack.getStackDepth() > 0) { - OpcodeStack.Item itm = stack.getStackItem(0); - groupId = findCollectionGroup(itm, true); - - } - } else if ("iterator()Ljava/util/Iterator;".equals(methodInfo)) { - if (stack.getStackDepth() > 0) { - OpcodeStack.Item itm = stack.getStackItem(0); - groupId = findCollectionGroup(itm, true); - } - } else if ("remove(Ljava/lang/Object;)Z".equals(methodInfo)) { - if (stack.getStackDepth() > 1) { - OpcodeStack.Item itm = stack.getStackItem(1); - int id = findCollectionGroup(itm, true); - if (id >= 0) { - Integer it = groupToIterator.get(Integer14.valueOf(id)); - Loop loop = loops.get(it); - if (loop != null) { - int pc = getPC(); - if (loop.hasPC(pc)) { - bugReporter.reportBug(new BugInstance(this, "DWI_DELETING_WHILE_ITERATING", NORMAL_PRIORITY) - .addClass(this) - .addMethod(this) - .addSourceLine(this)); - } - } - } - } - } else { - Integer numArgs = modifyingMethods.get(methodInfo); - if (numArgs != null) { - if (stack.getStackDepth() > numArgs.intValue()) { - OpcodeStack.Item itm = stack.getStackItem(numArgs.intValue()); - int id = findCollectionGroup(itm, true); - if (id >= 0) { - Integer it = groupToIterator.get(Integer14.valueOf(id)); - if (it != null) { - Loop loop = loops.get(it); - if (loop != null) { - int pc = getPC(); - if (loop.hasPC(pc)) { - bugReporter.reportBug(new BugInstance(this, "DWI_MODIFYING_WHILE_ITERATING", NORMAL_PRIORITY) - .addClass(this) - .addMethod(this) - .addSourceLine(this)); - } - } - } - } - } - } - } - } else if ("java/util/Iterator".equals(className) && "hasNext()Z".equals(methodInfo)) { - if (stack.getStackDepth() > 0) { - OpcodeStack.Item itm = stack.getStackItem(0); - Integer id = (Integer)itm.getUserValue(); - if (id != null) - groupId = id.intValue(); - } - } - } else if (seen == PUTFIELD) { - if (stack.getStackDepth() > 1) { - OpcodeStack.Item itm = stack.getStackItem(0); - - Integer id = (Integer)itm.getUserValue(); - if (id == null) { - FieldAnnotation fa = FieldAnnotation.fromFieldDescriptor(new FieldDescriptor(getClassConstantOperand(), getNameConstantOperand(), getSigConstantOperand(), false)); - itm = new OpcodeStack.Item(itm.getSignature(), fa, stack.getStackItem(1).getRegisterNumber()); - removeFromCollectionGroup(itm); - } - } - } 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 { - JavaClass cls = itm.getJavaClass(); - if ((cls != null) && cls.implementationOf(iteratorClass)) { - Integer regIt = Integer14.valueOf(reg); - Iterator<Integer> curIt = groupToIterator.values().iterator(); - while (curIt.hasNext()) { - if (curIt.next().equals(regIt)) - curIt.remove(); - } - groupToIterator.put(id, regIt); - } - - Set<Comparable> group = collectionGroups.get(id.intValue()); - if (group != null) { - group.add(Integer14.valueOf(reg)); - } - } catch (ClassNotFoundException cnfe) { - bugReporter.reportMissingClass(cnfe); - } - } - } - } else if ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3))) { - int reg = RegisterUtils.getALoadReg(this, seen); - OpcodeStack.Item itm = new OpcodeStack.Item(new OpcodeStack.Item(), reg); - groupId = findCollectionGroup(itm, false); - } else if (seen == IFEQ) { - if (stack.getStackDepth() > 0) { - OpcodeStack.Item itm = stack.getStackItem(0); - Integer id = (Integer)itm.getUserValue(); - if (id != null) { - int target = getBranchTarget(); - int gotoAddr = target - 3; - int ins = getCode().getCode()[gotoAddr]; - if (ins < 0) - ins = 256 + ins; - if (ins == GOTO) { - Integer reg = groupToIterator.get(id); - if (reg != null) - loops.put(reg, new Loop(getPC(), gotoAddr)); - } - } - } - } - } 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 Comparable getGroupElement(OpcodeStack.Item itm) { - Comparable groupElement = null; - - int reg = itm.getRegisterNumber(); - if (reg >= 0) - groupElement = Integer14.valueOf(reg); - else { - XField field = itm.getXField(); - if (field != null) { - int regLoad = itm.getFieldLoadedFromRegister(); - if (regLoad >= 0) - groupElement = field.getName() + ":{" + regLoad + "}"; - } - } - - return groupElement; - } - - private int findCollectionGroup(OpcodeStack.Item itm, boolean addIfNotFound) { - - Integer id = (Integer)itm.getUserValue(); - if (id != null) - return id.intValue(); - - Comparable groupElement = getGroupElement(itm); - 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; - } - } - - if (addIfNotFound) { - Set<Comparable> group = new HashSet<Comparable>(); - group.add(groupElement); - collectionGroups.add(group); - return collectionGroups.size() - 1; - } - } - - return -1; - } - - private void removeFromCollectionGroup(OpcodeStack.Item itm) { - Comparable groupElement = getGroupElement(itm); - if (groupElement != null) { - for (Set<? extends Comparable> group : collectionGroups) { - if (group.contains(groupElement)) { - group.remove(groupElement); - break; - } - } - } - } - - static class Loop - { - public int loopStart; - public int loopFinish; - - public Loop(int start, int finish) { - loopStart = start; - loopFinish = finish; - } - - public int getLoopFinish() { - return loopFinish; - } - - public int getLoopStart() { - return loopStart; - } - - public boolean hasPC(int pc) { - return (loopStart <= pc) && (pc <= loopFinish); - } - - @Override - public String toString() { - return "Start=" + loopStart + " Finish=" + loopFinish; - } - } -} +/* + * fb-contrib - Auxiliary detectors for Java programs + * Copyright (C) 2005-2007 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 distribu... [truncated message content] |