Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * fb-contrib - Auxiliary detectors for Java programs
   * Copyright (C) 2005-2013 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.List;
 import java.util.Map;
 import java.util.Set;
 
 import  org.apache.bcel.Constants;
 import  org.apache.bcel.Repository;
 import  org.apache.bcel.classfile.Code;
 import  org.apache.bcel.classfile.JavaClass;
 import  org.apache.bcel.classfile.LocalVariable;
 import  org.apache.bcel.classfile.LocalVariableTable;
 import  org.apache.bcel.generic.Type;
 
 
 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;
 import  edu.umd.cs.findbugs.ba.XField;
 import  edu.umd.cs.findbugs.classfile.FieldDescriptor;

looks for deletion of items from a collection using the remove method of the collection at the same time that the collection is being iterated on. If this occurs the iterator will become invalid and throw a ConcurrentModificationException. Instead, the remove should be called on the iterator itself.
 
 public class DeletingWhileIterating extends BytecodeScanningDetector
 {
 	private static JavaClass collectionClass;
 	private static JavaClass iteratorClass;
 	private static Set<JavaClass> exceptionClasses;
 	private static final Set<StringcollectionMethods;
 	static {
 		try {
 			 = Repository.lookupClass("java.util.Collection");
 			 = Repository.lookupClass("java.util.Iterator");
 		} catch (ClassNotFoundException cnfe) {
 			 = null;
 			 = null;
 		}
 
 		try {
 			 = new HashSet<JavaClass>();
 			.add(Repository.lookupClass("java.util.concurrent.CopyOnWriteArrayList"));
 			.add(Repository.lookupClass("java.util.concurrent.CopyOnWriteArraySet"));
 		} catch (ClassNotFoundException cnfe) {
 		}
 
 		.add("entrySet()Ljava/lang/Set;");
 		.add("keySet()Ljava/lang/Set;");
 		.add("values()Ljava/lang/Collection;");
 	}
 	private static final Map<StringIntegermodifyingMethods = new HashMap<StringInteger>();
 	static {
 		.put("add(Ljava/lang/Object;)Z", Integer.valueOf(1));
 		.put("addAll(Ljava/util/Collection;)Z", Integer.valueOf(1));
 		.put("addAll(ILjava/util/Collection;)Z", Integer.valueOf(2));
 		.put("clear()V", Integer.valueOf(0));
 		.put("remove(I)Ljava/lang/Object;", Integer.valueOf(1));
 		.put("removeAll(Ljava/util/Collection;)Z", Integer.valueOf(1));
 		.put("retainAll(Ljava/util/Collection;)Z", Integer.valueOf(1));
 	}
 
 	private BugReporter bugReporter;
 	private OpcodeStack stack;
 	private Map<IntegerLooploops;
 	private Map<IntegerSet<Integer>> endOfScopes;

constructs a DWI detector given the reporter to report bugs on

Parameters:
bugReporter the sync of bug reports
	public DeletingWhileIterating(BugReporter bugReporter) {
		this. = bugReporter;
	}

implements the visitor to setup the opcode stack, collectionGroups, groupToIterator and loops

Parameters:
classContext the context object of the currently parsed class
	public void visitClassContext(ClassContext classContext) {
		if (( == null) || ( == null))
			return;
		try {
			 = new OpcodeStack();
			 = new HashMap<IntegerLoop>();
			super.visitClassContext(classContext);
finally {
			 = null;
			 = null;
			 = null;
		}
	}

implements the visitor to reset the stack, collectionGroups, groupToIterator and loops

Parameters:
obj the context object of the currently parsed code block
	public void visitCode(Code obj) {
		.resetForMethodEntry(this);
		super.visitCode(obj);
	}

implements the visitor to look for deletes on collections that are being iterated

Parameters:
seen the opcode of the currently parsed instruction
	public void sawOpcode(int seen) {
		int groupId = -1;
		try {
			if (seen == INVOKEINTERFACE)
			{
				String methodName = getNameConstantOperand();
				String signature = getSigConstantOperand();
				String methodInfo = methodName + signature;
				if (isCollection(className)) {
					if (.contains(methodInfo)) {
						if (.getStackDepth() > 0) {
							OpcodeStack.Item itm = .getStackItem(0);
							groupId = findCollectionGroup(itmtrue);
						}
else if ("iterator()Ljava/util/Iterator;".equals(methodInfo)) {
						if (.getStackDepth() > 0) {
							OpcodeStack.Item itm = .getStackItem(0);
							groupId = findCollectionGroup(itmtrue);
						}
else if ("remove(Ljava/lang/Object;)Z".equals(methodInfo)) {
						if (.getStackDepth() > 1) {
							OpcodeStack.Item itm = .getStackItem(1);
							int id = findCollectionGroup(itmtrue);
							if (id >= 0) {
								Integer it = .get(Integer.valueOf(id));
								Loop loop = .get(it);
								if (loop != null) {
									int pc = getPC();
									if (loop.hasPC(pc)) {
										boolean breakFollows = breakFollows(loop, !Type.getReturnType(signature).getSignature().equals("V"));
										if (!breakFollows) {
											.reportBug(new BugInstance(this"DWI_DELETING_WHILE_ITERATING", NORMAL_PRIORITY)
														.addClass(this)
														.addMethod(this)
														.addSourceLine(this));
										}
									}
								}
							}
						}
else {
						Integer numArgs = .get(methodInfo);
						if (numArgs != null) {
							if (.getStackDepth() > numArgs.intValue()) {
								OpcodeStack.Item itm = .getStackItem(numArgs.intValue());
								int id = findCollectionGroup(itmtrue);
								if (id >= 0) {
									Integer it = .get(Integer.valueOf(id));
									if (it != null) {
										Loop loop = .get(it);
										if (loop != null) {
											int pc = getPC();
											if (loop.hasPC(pc)) {
			                                     boolean breakFollows = breakFollows(loop, !Type.getReturnType(signature).getSignature().equals("V"));
			                                     if (!breakFollows) {
			                                         .reportBug(new BugInstance(this"DWI_MODIFYING_WHILE_ITERATING", NORMAL_PRIORITY)
															.addClass(this)
															.addMethod(this)
															.addSourceLine(this));
			                                     }
											}
										}
									}
								}
							}
						}
					}
else if ("java/util/Iterator".equals(className) && "hasNext()Z".equals(methodInfo)) {
					if (.getStackDepth() > 0) {
						OpcodeStack.Item itm = .getStackItem(0);
						Integer id = (Integer)itm.getUserValue();
						if (id != null)
							groupId = id.intValue();
					}
				}
			 } else if (seen == PUTFIELD) {
				if (.getStackDepth() > 1) {
					OpcodeStack.Item itm = .getStackItem(0);
					Integer id = (Integer)itm.getUserValue();
					if (id == null) {
						FieldAnnotation fa = FieldAnnotation.fromFieldDescriptor(new FieldDescriptor(getClassConstantOperand(), getNameConstantOperand(), getSigConstantOperand(), false));
						itm = new OpcodeStack.Item(itm.getSignature(), fa.getStackItem(1).getRegisterNumber());
						removeFromCollectionGroup(itm);
					}
				}
			 } else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) {
				if (.getStackDepth() > 0) {
					OpcodeStack.Item itm = .getStackItem(0);
					Integer id = (Integer)itm.getUserValue();
					if (id != null) {
						int reg = RegisterUtils.getAStoreReg(thisseen);
						try {
                            JavaClass cls = itm.getJavaClass();
							if ((cls != null) && cls.implementationOf()) {
                                Integer regIt = Integer.valueOf(reg);
                                Iterator<IntegercurIt = .values().iterator();
                                while (curIt.hasNext()) {
                                    if (curIt.next().equals(regIt))
                                        curIt.remove();
                                }
								.put(idregIt);
							}
							if (group != null) {
								group.add(Integer.valueOf(reg));
							}
catch (ClassNotFoundException cnfe) {
							.reportMissingClass(cnfe);
						}
					}
				}
else if ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3))) {
				int reg = RegisterUtils.getALoadReg(thisseen);
				OpcodeStack.Item itm = new OpcodeStack.Item(new OpcodeStack.Item(), reg);
				groupId = findCollectionGroup(itmfalse);
else if (seen == IFEQ) {
				if (.getStackDepth() > 0) {
					OpcodeStack.Item itm = .getStackItem(0);
					Integer id = (Integer)itm.getUserValue();
					if (id != null) {
						int target = getBranchTarget();
						int gotoAddr = target - 3;
						int ins = getCode().getCode()[gotoAddr];
						if (ins < 0)
							ins = 256 + ins;
						if ((ins == GOTO) || (ins == GOTO_W)) {
							if (reg != null)
								.put(regnew Loop(getPC(), gotoAddr));
						}
					}
				}
			}
finally {
			TernaryPatcher.pre(seen);
			.sawOpcode(thisseen);
			TernaryPatcher.post(seen);
			if ((groupId >= 0) && (.getStackDepth() > 0)) {
				OpcodeStack.Item itm = .getStackItem(0);
				itm.setUserValue(Integer.valueOf(groupId));
			}
		}
	}
	private boolean breakFollows(Loop loopboolean needsPop) {
        byte[] code = getCode().getCode();
        int nextPC = getNextPC();
        if (needsPop) {
            int popOp = CodeByteUtils.getbyte(codenextPC++);
            if (popOp != Constants.POP) {
                return false;
            }
        }
        int gotoOp = CodeByteUtils.getbyte(codenextPC);
        if ((gotoOp == Constants.GOTO) || (gotoOp == Constants.GOTO_W)) {
            int target = nextPC + CodeByteUtils.getshort(codenextPC+1);
            if (target > loop.getLoopFinish())
                return true;
        }
        return false;
	}
	private boolean isCollection(String className) {
		try {
			JavaClass cls = Repository.lookupClass(className);
			return cls.implementationOf() && !.contains(cls);
catch (ClassNotFoundException cnfe) {
			.reportMissingClass(cnfe);
			return false;
		}
	}
	private Comparable<?> getGroupElement(OpcodeStack.Item itm) {
		Comparable<?> groupElement = null;
		int reg = itm.getRegisterNumber();
		if (reg >= 0)
			groupElement = Integer.valueOf(reg);
		else {
			XField field = itm.getXField();
			if (field != null) {
                int regLoad = itm.getFieldLoadedFromRegister();
                if (regLoad >= 0)
                    groupElement = field.getName() + ":{" + regLoad + "}";
			}
		}
		return groupElement;
	}
	private int findCollectionGroup(OpcodeStack.Item itmboolean addIfNotFound) {
		Integer id = (Integer)itm.getUserValue();
		if (id != null)
			return id.intValue();
		Comparable<?> groupElement = getGroupElement(itm);
		if (groupElement != null) {
			int numGroups = .size();
			for (int i = 0; i < numGroupsi++) {
				Set<? extends Comparable<?>> group = .get(i);
				if (group.contains(groupElement)) {
					return i;
				}
			}
			if (addIfNotFound) {
				Set<Comparable<?>> group = new HashSet<Comparable<?>>();
				group.add(groupElement);
				return .size() - 1;
			}
		}
		return -1;
	}
	private void removeFromCollectionGroup(OpcodeStack.Item itm) {
		Comparable<?> groupElement = getGroupElement(itm);
		if (groupElement != null) {
			for (Set<? extends Comparable<?>> group : ) {
				if (group.contains(groupElement)) {
					group.remove(groupElement);
					break;
				}
			}
		}
	}
	private void buildVariableEndScopeMap() {
	     = new HashMap<IntegerSet<Integer>>();
	    LocalVariableTable lvt = getMethod().getLocalVariableTable();
        if (lvt != null) {
            int len = lvt.getLength();
            for (int i = 0; i < leni++) {
            	@SuppressWarnings("deprecation")
                LocalVariable lv = lvt.getLocalVariable(i);
                if (lv != null) {
                    Integer endPC = Integer.valueOf(lv.getStartPC() + lv.getLength());
                    Set<Integervars = .get(endPC);
                    if (vars == null) {
                        vars = new HashSet<Integer>();
                        .put(endPCvars);
                    }
                    vars.add(Integer.valueOf(lv.getIndex()));
                }
            }
        }
	}
	private void processEndOfScopes(Integer pc) {
	    Set<IntegerendVars = .get(pc);
	    if (endVars != null) {
    	    for (Integer v : endVars) {
    	        Iterator<Set<Comparable<?>>> it = .iterator();
    	        while (it.hasNext()) {
    	            Set<Comparable<?>> gv = it.next();
    	            if (gv.contains(v)) {
    	                gv.remove(v);
    	            }
    	        }
    	    }
	    }
	}
	static class Loop
	{
		public int loopStart;
		public int loopFinish;
		public Loop(int startint finish) {
			 = start;
			 = finish;
		}
		public int getLoopFinish() {
			return ;
		}
		public int getLoopStart() {
			return ;
		}
		public boolean hasPC(int pc) {
			return ( <= pc) && (pc <= );
		}
        @Override
        public String toString() {
            return "Start=" +  + " Finish=" + ;
        }
	}
New to GrepCode? Check out our FAQ X