gt: 					+ " (" + next + ")"
context: /*
   * FindBugs - Find bugs in Java programs
   * Copyright (C) 2003,2004 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 edu.umd.cs.findbugs.detect;
  
  
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  
  import org.apache.bcel.classfile.Code;
  import org.apache.bcel.classfile.Field;
  import org.apache.bcel.classfile.Method;
  
  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.IntAnnotation;
  import edu.umd.cs.findbugs.SystemProperties;
  
  public class LockedFields extends BytecodeScanningDetector {
  	private static final boolean DEBUG = SystemProperties.getBoolean("lockedfields.debug");
  
  	Set<FieldAnnotation> volatileOrFinalFields = new HashSet<FieldAnnotation>();
  	Set<FieldAnnotation> fieldsWritten = new HashSet<FieldAnnotation>();
  	Set<FieldAnnotation> fieldsRead = new HashSet<FieldAnnotation>();
  	Set<FieldAnnotation> localLocks = new HashSet<FieldAnnotation>();
  	Set<FieldAnnotation> publicFields = new HashSet<FieldAnnotation>();
  	Set<FieldAnnotation> writtenOutsideOfConstructor = new HashSet<FieldAnnotation>();
  	boolean synchronizedMethod;
  	boolean publicMethod;
  	boolean protectedMethod;
  	// boolean privateMethod;
  	boolean inConstructor;
  	Map<FieldAnnotation, int[]> stats = new TreeMap<FieldAnnotation, int[]>();
  	int state;
  	boolean thisOnTopOfStack;
  	boolean doubleThisOnTopOfStack;
  	boolean thisLocked;
  	boolean thisLockingOnly = true;
  
  	private BugReporter bugReporter;
  
  	static final int READ_LOCKED = 0;
  	static final int WRITTEN_LOCKED = 1;
  	static final int READ_UNLOCKED = 2;
  	static final int WRITTEN_UNLOCKED = 3;
  
  	static final String[] names = {
  		"R/L",
  		"W/L",
  		"R/U",
  		"W/U"};
  
  	public LockedFields(BugReporter bugReporter) {
  		this.bugReporter = bugReporter;
  	}
  
  	private void updateStats(Set<FieldAnnotation> fields, int mode) {
  		// if (privateMethod) return;
  		if (!publicMethod && !protectedMethod) {
  			if (mode == READ_UNLOCKED || mode == WRITTEN_UNLOCKED) return;
  		}
  		/*
  		if (!publicMethod) {
  			if (mode == READ_UNLOCKED || mode == WRITTEN_UNLOCKED) return;
  			}
  		*/
  		for (FieldAnnotation f : fields) {
  			if (f.getClassName().equals(getDottedClassName()) && mode <= WRITTEN_LOCKED)
  				localLocks.add(f);
  			int[] theseStats = stats.get(f);
  			if (theseStats == null) {
  				theseStats = new int[4];
  				stats.put(f, theseStats);
  			}
  			if (DEBUG)
  				System.out.println(names[mode]
  						+ "	"
  						+ getFullyQualifiedMethodName()
  						+ "	"
  						+ f.toString());
  
  			theseStats[mode]++;
  		}
  	}
  
  	@Override
  		 public void visit(Field obj) {
  		super.visit(obj);
  
  		FieldAnnotation f = FieldAnnotation.fromVisitedField(this);
  
  		int flags = obj.getAccessFlags();
  		boolean publicField = (flags & ACC_PUBLIC) != 0;
  		boolean volatileField = (flags & ACC_VOLATILE) != 0;
  		boolean finalField = (flags & ACC_FINAL) != 0;
  		if (publicField)
  			publicFields.add(f);
  		if (volatileField || finalField)
  			volatileOrFinalFields.add(f);
  	}
  
  	@Override
  		 public void visit(Method obj) {
  		super.visit(obj);
  		int flags = obj.getAccessFlags();
  		publicMethod = (flags & ACC_PUBLIC) != 0;
  		protectedMethod = (flags & ACC_PROTECTED) != 0;
  		synchronizedMethod = (flags & ACC_SYNCHRONIZED) != 0;
  		if (synchronizedMethod)
  			state = 1;
  		else
  			state = 0;
  		fieldsWritten.clear();
  		fieldsRead.clear();
  		inConstructor = getMethodName().equals("<init>")
  				|| getMethodName().equals("<clinit>")
  				|| getMethodName().equals("readObject")
  				|| getMethodName().equals("clone")
  				|| getMethodName().equals("close")
  				|| getMethodName().equals("finalize");
  		/*
  		privateMethod = (flags & ACC_PRIVATE) != 0
  				|| methodName.startsWith("access$");
  		*/
  
  	}
  
  
  	@Override
  		 public void visit(Code obj) {
  		if (inConstructor) return;
  		thisOnTopOfStack = false;
  		thisLocked = false;
  		super.visit(obj);
  		// System.out.println("End of method, state = " + state);
  		if (state == 1) {
  			updateStats(fieldsWritten, WRITTEN_LOCKED);
  			updateStats(fieldsRead, READ_LOCKED);
  		} else if (obj.getCode().length > 6) {
  			updateStats(fieldsWritten, WRITTEN_UNLOCKED);
  			updateStats(fieldsRead, READ_UNLOCKED);
  		}
  	}
  
  	@Override
  		 public void sawOpcode(int seen) {
  		// state: 0 - unlocked
  		// state: 1 - locked
  		// state: 2 - saw unlocked, but might still be locked
  
  		switch (seen) {
  		case ASTORE_1:
  		case ASTORE_2:
  		case ASTORE_3:
  		case ASTORE:
  			thisOnTopOfStack = doubleThisOnTopOfStack;
  			return;
  		case ALOAD_0:
  			thisOnTopOfStack = true;
  			return;
  		case DUP:
  			doubleThisOnTopOfStack = thisOnTopOfStack;
  			return;
  		case MONITOREXIT:
  			if (thisLockingOnly && !thisLocked) break;
  			updateStats(fieldsWritten, WRITTEN_LOCKED);
  			updateStats(fieldsRead, READ_LOCKED);
  			state = 2;
  			// System.out.println("monitorexit	" + thisLocked);
  			fieldsWritten.clear();
  			fieldsRead.clear();
  			break;
  		case MONITORENTER:
  			thisLocked = thisOnTopOfStack;
  			if (thisLockingOnly && !thisLocked) break;
  			updateStats(fieldsWritten, WRITTEN_UNLOCKED);
  			updateStats(fieldsRead, READ_UNLOCKED);
  			// System.out.println("monitorenter	" + thisLocked);
  			state = 1;
  			fieldsWritten.clear();
  			fieldsRead.clear();
  			break;
  		case PUTFIELD:
  			{
  				FieldAnnotation f = FieldAnnotation.fromReferencedField(this);
  				writtenOutsideOfConstructor.add(f);
  				if (!getClassName().equals(getClassConstantOperand())) break;
  				// System.out.println("putfield	" + f + ", state = " + state);
  				fieldsWritten.add(f);
  			}
  			break;
  		case GETFIELD:
  			int next = codeBytes[getPC() + 3] & 0xff;
  			if (!thisOnTopOfStack) break;
  			if (next != IFNULL && next != IFNONNULL) {
  				FieldAnnotation f = FieldAnnotation.fromReferencedField(this);
  				// System.out.println("getfield	" + f);
  				fieldsRead.add(f);
  				/*
  				System.out.println("After read of "
  					+ classConstant + "." + nameConstant
  					+ ", next PC is " + (PC+3)
  					);
  				System.out.println("After read of "
  					+ classConstant + "." + nameConstant
  					+ ", next opcode is " + OPCODE_NAMES[next]
  