[Fb-contrib-commit] SF.net SVN: fb-contrib: [548] trunk/fb-contrib/etc
Brought to you by:
dbrosius
From: <dbr...@us...> - 2006-06-06 04:13:31
|
Revision: 548 Author: dbrosius Date: 2006-06-05 21:13:16 -0700 (Mon, 05 Jun 2006) ViewCVS: http://svn.sourceforge.net/fb-contrib/?rev=548&view=rev Log Message: ----------- Initial Checkin new RMC detector. Likely to have many false positives at present. Modified Paths: -------------- trunk/fb-contrib/etc/findbugs.xml trunk/fb-contrib/etc/messages.xml Added Paths: ----------- trunk/fb-contrib/samples/RMC_Sample.java trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/RedundantMethodCalls.java Modified: trunk/fb-contrib/etc/findbugs.xml =================================================================== --- trunk/fb-contrib/etc/findbugs.xml 2006-06-04 03:43:32 UTC (rev 547) +++ trunk/fb-contrib/etc/findbugs.xml 2006-06-06 04:13:16 UTC (rev 548) @@ -207,6 +207,10 @@ speed="fast" reports="SACM_STATIC_ARRAY_CREATED_IN_METHOD" /> + <Detector class="com.mebigfatguy.fbcontrib.detect.RedundantMethodCalls" + speed="fast" + reports="RMC_REDUNDANT_METHOD_CALLS" /> + <!-- BugPattern --> <BugPattern abbrev="ISB" type="ISB_INEFFICIENT_STRING_BUFFERING" category="PERFORMANCE" /> @@ -261,4 +265,5 @@ <BugPattern abbrev="NMCS" type="NMCS_NEEDLESS_MEMBER_COLLECTION_SYNCHRONIZATION" category="PERFORMANCE" experimental="true" /> <BugPattern abbrev="ITC" type="ITC_INHERITANCE_TYPE_CHECKING" category="STYLE" experimental="true" /> <BugPattern abbrev="SACM" type="SACM_STATIC_ARRAY_CREATED_IN_METHOD" category="PERFORMANCE" experimental="true" /> + <BugPattern abbrev="RMC" type="RMC_REDUNDANT_METHOD_CALLS" category="PERFORMANCE" experimental="true" /> </FindbugsPlugin> \ No newline at end of file Modified: trunk/fb-contrib/etc/messages.xml =================================================================== --- trunk/fb-contrib/etc/messages.xml 2006-06-04 03:43:32 UTC (rev 547) +++ trunk/fb-contrib/etc/messages.xml 2006-06-06 04:13:16 UTC (rev 548) @@ -589,7 +589,17 @@ <p>It is a fast detector</p> ]]> </Details> - </Detector> + </Detector> + + <Detector class="com.mebigfatguy.fbcontrib.detect.RedundantMethodCalls"> + <Details> + <![CDATA[ + <p>looks for calls of the same method on the same object when that object hasn't changed. + This often is redundant, and the second call can be removed, or combined.</p> + <p>It is a fast detector</p> + ]]> + </Details> + </Detector> <!-- BugPattern --> @@ -1270,6 +1280,20 @@ </Details> </BugPattern> + <BugPattern type="RMC_REDUNDANT_METHOD_CALLS"> + <ShortDescription>Method calls the same method on the same object redundantly</ShortDescription> + <LongDescription>Method {1} calls the same method on the same object redundantly</LongDescription> + <Details> + <![CDATA[ + <p>This method makes two consecutive calls to the same method using the same constant + parameters, on the same instance without any intervening changes to the objects. If this + method does not make changes to the object, which it appears it doesn't, then making + two calls is just a waste. These method calls could be combined by assigning the + result into a temporary, and using the temporary the second time. + ]]> + </Details> + </BugPattern> + <!-- BugCode --> <BugCode abbrev="ISB">Inefficient String Buffering</BugCode> @@ -1320,4 +1344,5 @@ <BugCode abbrev="NMCS">Needless Member Collection Synchronization</BugCode> <BugCode abbrev="ITC">Inheritance Type Checking</BugCode> <BugCode abbrev="SACM">Static Array Created in Method</BugCode> + <BugCode abbrev="RMC">Redundant Method Calls</BugCode> </MessageCollection> \ No newline at end of file Added: trunk/fb-contrib/samples/RMC_Sample.java =================================================================== --- trunk/fb-contrib/samples/RMC_Sample.java (rev 0) +++ trunk/fb-contrib/samples/RMC_Sample.java 2006-06-06 04:13:16 UTC (rev 548) @@ -0,0 +1,14 @@ +import java.util.Calendar; +import java.util.Date; + +public class RMC_Sample +{ + public boolean test1(Calendar c) + { + Date d = c.getTime(); + long l = d.getTime(); + Date e = c.getTime(); + long j = e.getTime(); + return l == j; + } +} Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/RedundantMethodCalls.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/RedundantMethodCalls.java (rev 0) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/RedundantMethodCalls.java 2006-06-06 04:13:16 UTC (rev 548) @@ -0,0 +1,167 @@ +package com.mebigfatguy.fbcontrib.detect; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.bcel.classfile.Code; +import org.apache.bcel.generic.Type; + +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; + +/** + * looks for calls of the same method on the same object when that object hasn't changed. + * This often is redundant, and the second call can be removed, or combined. + */ +public class RedundantMethodCalls extends BytecodeScanningDetector +{ + private static Set<String> riskyMethodNameContents = new HashSet<String>(); + static { + riskyMethodNameContents.add("next"); + riskyMethodNameContents.add("add"); + riskyMethodNameContents.add("put"); + riskyMethodNameContents.add("remove"); + } + private BugReporter bugReporter; + private OpcodeStack stack = null; + private Map<Integer, MethodCall> localMethodCalls = null; + private Map<String, MethodCall> fieldMethodCalls = null; + private Set<Integer> branchTargets = null; + + public RedundantMethodCalls(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + public void visitClassContext(ClassContext classContext) { + try { + stack = new OpcodeStack(); + localMethodCalls = new HashMap<Integer, MethodCall>(); + fieldMethodCalls = new HashMap<String, MethodCall>(); + branchTargets = new HashSet<Integer>(); + super.visitClassContext(classContext); + } finally { + stack = null; + localMethodCalls = null; + fieldMethodCalls = null; + branchTargets = null; + } + } + public void visitCode(Code obj) { + stack.resetForMethodEntry(this); + localMethodCalls.clear(); + fieldMethodCalls.clear(); + super.visitCode(obj); + } + + public void sawOpcode(int seen) { + try { + stack.mergeJumps(this); + if (branchTargets.remove(Integer14.valueOf(getPC()))) { + localMethodCalls.clear(); + fieldMethodCalls.clear(); + } + + if (((seen >= IFEQ) && (seen <= GOTO)) || ((seen >= IFNULL) && (seen <= GOTO_W))) { + branchTargets.add(Integer14.valueOf(getBranchTarget())); + } else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) { + localMethodCalls.remove(Integer14.valueOf(RegisterUtils.getAStoreReg(this, seen))); + } else if (seen == PUTFIELD) { + fieldMethodCalls.remove(getNameConstantOperand()); + } else if ((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) { + String methodName = getNameConstantOperand(); + String signature = getSigConstantOperand(); + int parmCount = Type.getArgumentTypes(signature).length; + if (stack.getStackDepth() > parmCount) { + Object[] parmConstants = new Object[parmCount]; + for (int i = 0; i < parmCount; i++) { + OpcodeStack.Item parm = stack.getStackItem(i); + parmConstants[i] = parm.getConstant(); + if (parmConstants[i] == null) + return; + } + OpcodeStack.Item obj = stack.getStackItem(parmCount); + int reg = obj.getRegisterNumber(); + FieldAnnotation fa = obj.getField(); + + MethodCall mc; + if (reg >= 0) { + mc = localMethodCalls.get(Integer14.valueOf(reg)); + } else if (fa != null) { + mc = fieldMethodCalls.get(fa.getFieldName()); + } else + return; + + if (mc != null) { + if (!signature.endsWith("V") && methodName.equals(mc.getName()) && signature.equals(mc.getSignature()) && !isRiskyName(methodName)) { + Object[] parms = mc.getParms(); + if (Arrays.equals(parms, parmConstants)) { + bugReporter.reportBug(new BugInstance(this, "RMC_REDUNDANT_METHOD_CALLS", NORMAL_PRIORITY) + .addClass(this) + .addMethod(this) + .addSourceLine(this)); + } + } + if (reg >= 0) { + localMethodCalls.remove(Integer14.valueOf(reg)); + } else { + fieldMethodCalls.remove(fa.getFieldName()); + } + } else { + if (reg >= 0) { + localMethodCalls.put(Integer14.valueOf(reg), new MethodCall(methodName, signature, parmConstants)); + } else if (fa != null) { + fieldMethodCalls.put(fa.getFieldName(), new MethodCall(methodName, signature, parmConstants)); + } + } + } + } + } finally { + stack.sawOpcode(this, seen); + } + } + + private boolean isRiskyName(String methodName) { + methodName = methodName.toLowerCase(Locale.ENGLISH); + for (String riskyName : riskyMethodNameContents) { + if (methodName.indexOf(riskyName) >= 0) + return true; + } + return false; + } + + private static class MethodCall + { + private String methodName; + private String methodSignature; + private Object[] methodParms; + + private MethodCall(String name, String signature, Object[] parms) { + methodName = name; + methodSignature = signature; + methodParms = parms; + } + + private String getName() { + return methodName; + } + + private String getSignature() { + return methodSignature; + } + + private Object[] getParms() { + return methodParms; + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |