[Fb-contrib-commit] SF.net SVN: fb-contrib: [520] trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/det
Brought to you by:
dbrosius
From: <dbr...@us...> - 2006-05-09 06:48:29
|
Revision: 520 Author: dbrosius Date: 2006-05-08 23:48:21 -0700 (Mon, 08 May 2006) ViewCVS: http://svn.sourceforge.net/fb-contrib/?rev=520&view=rev Log Message: ----------- Initial checkin - NMCS - far from working Added Paths: ----------- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java =================================================================== --- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java (rev 0) +++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/NeedlessMemberCollectionSynchronization.java 2006-05-09 06:48:21 UTC (rev 520) @@ -0,0 +1,206 @@ +/* + * 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.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.bcel.Repository; +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.classfile.JavaClass; + +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 private collection members, either static or instance, that are only initialized in + * the clinit or init, but are synchronized. This is not necessary as the constructor or static + * initializer are guaranteed to be thread safe. + */ +public class NeedlessMemberCollectionSynchronization extends BytecodeScanningDetector { + private static JavaClass collectionClass; + static { + try { + collectionClass = Repository.lookupClass("java/util/Collection"); + } catch (ClassNotFoundException cnfe) { + collectionClass = null; + } + } + private static Set<String> syncCollections = new HashSet<String>(); + static { + syncCollections.add("java/util/Vector"); + syncCollections.add("java/util/Hashtable"); + } + private static final int IN_METHOD = 0; + private static final int IN_CLINIT = 1; + private static final int IN_INIT = 2; + + private BugReporter bugReporter; + private Map<String, FieldAnnotation> staticCollectionFields; + private Map<String, FieldAnnotation> instanceCollectionFields; + private Map<Integer, String> staticAliases; + private Map<Integer, String> instanceAliases; + private OpcodeStack stack; + private int state; + + /** + * constructs a NMCS detector given the reporter to report bugs on + * @param bugReporter the sync of bug reports + */ + public NeedlessMemberCollectionSynchronization(BugReporter bugReporter) { + this.bugReporter = bugReporter; + } + + @Override + public void visitClassContext(ClassContext classContext) { + try { + if (collectionClass != null) { + staticCollectionFields = new HashMap<String, FieldAnnotation>(); + instanceCollectionFields = new HashMap<String, FieldAnnotation>(); + staticAliases = new HashMap<Integer, String>(); + instanceAliases = new HashMap<Integer, String>(); + stack = new OpcodeStack(); + super.visitClassContext(classContext); + } + } finally { + staticCollectionFields = null; + instanceCollectionFields = null; + staticAliases = null; + instanceAliases = null; + stack = null; + } + } + + @Override + public void visitField(Field obj) { + if (obj.isPrivate()) { + String signature = obj.getSignature(); + if (signature.charAt(0) == 'L') { + try { + JavaClass cls = Repository.lookupClass(signature); + if (cls.implementationOf(collectionClass)) { + FieldAnnotation fa = FieldAnnotation.fromVisitedField(this); + if (obj.isStatic()) + staticCollectionFields.put(fa.getFieldName(), fa); + else + instanceCollectionFields.put(fa.getFieldName(), fa); + } + } catch (ClassNotFoundException cnfe) { + bugReporter.reportMissingClass(cnfe); + } + } + } + } + + @Override + public void visitCode(Code obj) { + if ((staticCollectionFields.size() + instanceCollectionFields.size()) > 0) { + staticAliases.clear(); + instanceAliases.clear(); + String methodName = getMethodName(); + if ("<clinit>".equals(methodName)) + state = IN_CLINIT; + else if ("<init>".equals(methodName)) + state = IN_INIT; + else + state = IN_METHOD; + stack.resetForMethodEntry(this); + super.visitCode(obj); + } + } + + @Override + public void sawOpcode(int seen) { + switch (state) { + case IN_CLINIT: + sawCLInitOpcode(seen); + break; + + case IN_INIT: + sawInitOpcode(seen); + break; + + case IN_METHOD: + sawMethodOpcode(seen); + } + } + + private void sawCLInitOpcode(int seen) { + boolean isSyncCollection = false; + try { + stack.mergeJumps(this); + isSyncCollection = isSyncCollectionCreation(seen); + } finally { + stack.sawOpcode(this, seen); + if (isSyncCollection) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item item = stack.getStackItem(0); + item.setUserValue(Boolean.TRUE); + } + } + } + } + + private void sawInitOpcode(int seen) { + boolean isSyncCollection = false; + try { + stack.mergeJumps(this); + isSyncCollection = isSyncCollectionCreation(seen); + } finally { + stack.sawOpcode(this, seen); + if (isSyncCollection) { + if (stack.getStackDepth() > 0) { + OpcodeStack.Item item = stack.getStackItem(0); + item.setUserValue(Boolean.TRUE); + } + } + } + } + + private void sawMethodOpcode(int seen) { + try { + stack.mergeJumps(this); + if (isSyncCollectionCreation(seen)) { + + } + } finally { + stack.sawOpcode(this, seen); + } + + } + + private boolean isSyncCollectionCreation(int seen) { + if (seen == INVOKESPECIAL) { + if ("<init>".equals(getNameConstantOperand())) { + return (syncCollections.contains(getClassConstantOperand())); + } + } else if (seen == INVOKESTATIC) { + String methodName = getNameConstantOperand(); + return "java/util/Collections".equals(getClassConstantOperand()) + && ("synchronizedMap".equals(methodName) || "synchronizedSet".equals(methodName)); + } + return false; + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |