[Fb-contrib-commit] SF.net SVN: fb-contrib: [745] trunk/fb-contrib/src/com/mebigfatguy/ fbcontrib/
Brought to you by:
dbrosius
From: <dbr...@us...> - 2006-12-21 09:10:41
|
Revision: 745 http://svn.sourceforge.net/fb-contrib/?rev=745&view=rev Author: dbrosius Date: 2006-12-21 01:10:40 -0800 (Thu, 21 Dec 2006) Log Message: ----------- report all calls to possibly overridable methods that are chained from a constructor. Modified Paths: -------------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PartiallyConstructedObjectAccess.java Modified: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PartiallyConstructedObjectAccess.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PartiallyConstructedObjectAccess.java 2006-12-21 07:47:41 UTC (rev 744) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PartiallyConstructedObjectAccess.java 2006-12-21 09:10:40 UTC (rev 745) @@ -18,6 +18,13 @@ */ package com.mebigfatguy.fbcontrib.detect; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.bcel.Constants; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.JavaClass; @@ -28,6 +35,7 @@ import edu.umd.cs.findbugs.BugReporter; import edu.umd.cs.findbugs.BytecodeScanningDetector; import edu.umd.cs.findbugs.OpcodeStack; +import edu.umd.cs.findbugs.SourceLineAnnotation; import edu.umd.cs.findbugs.ba.ClassContext; /** @@ -39,7 +47,9 @@ { private BugReporter bugReporter; private OpcodeStack stack; + private Map<Method, Map<Method, SourceLineAnnotation>> methodToCalledMethods; private boolean reportedCtor; + private boolean isCtor; /** * constructs a PCOA detector given the reporter to report bugs on @@ -49,25 +59,45 @@ this.bugReporter = bugReporter; } + /** + * implements the visitor to set up the stack and methodToCalledmethods map + * reports calls to public non final methods from methods called from constructors. + * + * @param classContext the context object of the currently parsed class + */ @Override public void visitClassContext(final ClassContext classContext) { try { JavaClass cls = classContext.getJavaClass(); if ((cls.getAccessFlags() & Constants.ACC_FINAL) == 0) { stack = new OpcodeStack(); + methodToCalledMethods = new HashMap<Method, Map<Method, SourceLineAnnotation>>(); super.visitClassContext(classContext); + + if (methodToCalledMethods.size() > 0) + reportChainedMethods(); } } finally { stack = null; + methodToCalledMethods = null; } } @Override public void visitCode(final Code obj) { stack.resetForMethodEntry(this); - reportedCtor = false; - if ("<init>".equals(getMethodName())) + String methodName = getMethodName(); + isCtor = "<init>".equals(methodName); + + if (!"<clinit>".equals(methodName)) { + Method m = getMethod(); + methodToCalledMethods.put(m, new HashMap<Method, SourceLineAnnotation>()); + reportedCtor = false; + super.visitCode(obj); + if (reportedCtor || (methodToCalledMethods.get(m).size() == 0)) + methodToCalledMethods.remove(getMethod()); + } } @Override @@ -78,7 +108,7 @@ try { stack.mergeJumps(this); - if ((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) { + if ((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE) || (seen == INVOKESPECIAL)) { int parmCount = Type.getArgumentTypes(getSigConstantOperand()).length; if (stack.getStackDepth() > parmCount) { OpcodeStack.Item itm = stack.getStackItem(parmCount); @@ -88,11 +118,18 @@ Method m = findMethod(cls, getNameConstantOperand(), getSigConstantOperand()); if (m != null) { if ((m.getAccessFlags() & Constants.ACC_FINAL) == 0) { - bugReporter.reportBug( new BugInstance(this, "PCOA_PARTIALLY_CONSTRUCTED_OBJECT_ACCESS", NORMAL_PRIORITY) - .addClass(this) - .addMethod(this) - .addSourceLine(this, getPC())); - reportedCtor = true; + if (isCtor && (seen != INVOKESPECIAL)) { + bugReporter.reportBug( new BugInstance(this, "PCOA_PARTIALLY_CONSTRUCTED_OBJECT_ACCESS", NORMAL_PRIORITY) + .addClass(this) + .addMethod(this) + .addSourceLine(this, getPC())); + reportedCtor = true; + } else { + if (!"<init>".equals(m.getName())) { + Map<Method, SourceLineAnnotation> calledMethods = methodToCalledMethods.get(getMethod()); + calledMethods.put(m, SourceLineAnnotation.fromVisitedInstruction(this)); + } + } } } } @@ -116,4 +153,51 @@ return null; } + + private void reportChainedMethods() { + Set<Method> checkedMethods = new HashSet<Method>(); + + JavaClass cls = getClassContext().getJavaClass(); + for (Map.Entry<Method, Map<Method, SourceLineAnnotation>> entry : methodToCalledMethods.entrySet()) { + Method m = entry.getKey(); + if ("<init>".equals(m.getName())) { + checkedMethods.clear(); + List<SourceLineAnnotation> slas = foundPrivateInChain(m, checkedMethods); + if (slas != null) { + BugInstance bi = new BugInstance(this, "PCOA_PARTIALLY_CONSTRUCTED_OBJECT_ACCESS", LOW_PRIORITY) + .addClass(cls) + .addMethod(cls, m); + for (SourceLineAnnotation sla : slas) + bi.addSourceLine(sla); + bugReporter.reportBug(bi); + } + } + } + } + + private List<SourceLineAnnotation> foundPrivateInChain(Method m, Set<Method> checkedMethods) { + Map<Method, SourceLineAnnotation> calledMethods = methodToCalledMethods.get(m); + if (calledMethods != null) { + for (Map.Entry<Method, SourceLineAnnotation> entry : calledMethods.entrySet()) { + Method cm = entry.getKey(); + if (checkedMethods.contains(cm)) + continue; + + if (!cm.isPrivate() && (cm.getAccessFlags() & Constants.ACC_FINAL) == 0) { + List<SourceLineAnnotation> slas = new LinkedList<SourceLineAnnotation>(); + slas.add(entry.getValue()); + return slas; + } + + checkedMethods.add(cm); + List<SourceLineAnnotation> slas = foundPrivateInChain(cm, checkedMethods); + if (slas != null) { + slas.add(0, entry.getValue()); + return slas; + } + } + } + + return null; + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |