Thread: [Fb-contrib-commit] fb-contrib/src/com/mebigfatguy/fbcontrib/detect FieldCouldBeLocal.java,NONE,1.1
Brought to you by:
dbrosius
|
From: Dave B. <dbr...@us...> - 2006-03-27 05:56:58
|
Update of /cvsroot/fb-contrib/fb-contrib/src/com/mebigfatguy/fbcontrib/detect In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16920/src/com/mebigfatguy/fbcontrib/detect Added Files: FieldCouldBeLocal.java Log Message: Initial checkin - new FCBL detector --- NEW FILE: FieldCouldBeLocal.java --- /* * FindBugs - Find bugs in Java programs * Copyright (C) 2005 Dave Brosius <dbr...@us...> * Copyright (C) 2005 University of Maryland * * 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.BitSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.FieldInstruction; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionHandle; 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.StatelessDetector; import edu.umd.cs.findbugs.ba.BasicBlock; import edu.umd.cs.findbugs.ba.CFG; import edu.umd.cs.findbugs.ba.ClassContext; import edu.umd.cs.findbugs.ba.Edge; import edu.umd.cs.findbugs.ba.BasicBlock.InstructionIterator; /** * finds fields that are used in a locals only fashion, specifically private fields or protected fields * in final classes that are accessed first in each method with a store vs. a load. */ public class FieldCouldBeLocal extends BytecodeScanningDetector implements StatelessDetector { private BugReporter bugReporter; private ClassContext clsContext; private Set<FieldAnnotation> localizableFields = new HashSet<FieldAnnotation>(); private CFG cfg; private ConstantPoolGen cpg; private BitSet visitedBlocks = new BitSet(); /** * constructs a FCBL detector given the reporter to report bugs on. * @param bugReporter the sync of bug reports */ public FieldCouldBeLocal(BugReporter bugReporter) { this.bugReporter = bugReporter; } /** * clone this detector so that it can be a StatelessDetector. * * @return a clone of this object * @throws CloneNotSupportedException should not happen */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * overrides the visitor to collect localizable fields, and then report those that * survive all method checks. * * @param classContext the context object that holds the JavaClass parsed */ @Override public void visitClassContext(ClassContext classContext) { localizableFields.clear(); clsContext = classContext; JavaClass cls = classContext.getJavaClass(); boolean clsIsFinal = cls.isFinal(); Field[] fields = cls.getFields(); for (Field f : fields) { if ((!f.isStatic()) && (f.isPrivate() || (clsIsFinal && f.isProtected()))) { FieldAnnotation fa = new FieldAnnotation(cls.getClassName(), f.getName(), f.getSignature(), false); localizableFields.add(fa); } } if (localizableFields.size() > 0) { super.visitClassContext(classContext); for (FieldAnnotation fa : localizableFields) { bugReporter.reportBug(new BugInstance(this, "FCBL_FIELD_COULD_BE_LOCAL", NORMAL_PRIORITY) .addClass(this) .addField(fa)); } } } /** * overrides the visitor to navigate basic blocks looking for all first usages of fields, removing * those that are read from first. * * @param obj the context object of the currently parsed method */ public void visitMethod(Method obj) { if (localizableFields.size() == 0) return; String methodName = obj.getName(); if ("<clinit>".equals(methodName) || "<init>".equals(methodName)) return; try { cpg = new ConstantPoolGen(getConstantPool()); cfg = clsContext.getCFG(obj); BasicBlock bb = cfg.getEntry(); Set<FieldAnnotation> uncheckedFields = new HashSet<FieldAnnotation>(localizableFields); visitedBlocks.clear(); checkBlock(bb, uncheckedFields); } catch (Exception e) { localizableFields.clear(); } } /** * implements the visitor to do nothing * * @param obj the context object of the currently parsed code attribute */ public void visitCode(Code obj) { } /** * looks in this basic block for the first access to the fields in uncheckedFields. Once found * the item is removed from uncheckedFields, and removed from localizableFields if the access is * a GETFIELD. If any unchecked fields remain, this method is recursively called on all outgoing edges * of this basic block. * * @param bb this basic block * @param uncheckedFields the list of fields to look for */ private void checkBlock(BasicBlock bb, Set<FieldAnnotation> uncheckedFields) { visitedBlocks.set(bb.getId()); InstructionIterator ii = bb.instructionIterator(); while (ii.hasNext()) { InstructionHandle ih = ii.next(); Instruction ins = ih.getInstruction(); if (ins instanceof FieldInstruction) { FieldInstruction fi = (FieldInstruction) ins; FieldAnnotation fa = new FieldAnnotation(getClassName(), fi.getFieldName(cpg), fi.getSignature(cpg), false); uncheckedFields.remove(fa); if (ins instanceof GETFIELD) localizableFields.remove(fa); } } if (uncheckedFields.size() > 0) { Iterator<Edge> oei = cfg.outgoingEdgeIterator(bb); while (oei.hasNext()) { Edge e = oei.next(); BasicBlock cb = e.getTarget(); if (!visitedBlocks.get(cb.getId())) { Set<FieldAnnotation> subCheckedFields = new HashSet<FieldAnnotation>(uncheckedFields); checkBlock(cb, subCheckedFields); if (localizableFields.size() == 0) return; } } } } } |