Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * fb-contrib - Auxiliary detectors for Java programs
   * Copyright (C) 2005-2014 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.Map;
 import java.util.Set;
 
 import  org.apache.bcel.classfile.Code;
 import  org.apache.bcel.classfile.CodeException;
 import  org.apache.bcel.classfile.LineNumber;
 import  org.apache.bcel.classfile.LineNumberTable;
 import  org.apache.bcel.generic.Type;
 
 
looks for calls of the same method on the same object when that object hasn't changed. This often is redundant, and the second call can be removed, or combined.
 
 public class PossiblyRedundantMethodCalls extends BytecodeScanningDetector
 {
     public static final String PRMC_RISKY_FIELD_USER_KEY = "fbcontrib.PRMC.riskynames";
     public static final String PRMC_RISKY_CLASS_USER_KEY = "fbcontrib.PRMC.riskyclasses";
     public static final String PRMC_HIGH_BYTECOUNT = "fbcontrib.PRMC.highbytecount";
     public static final String PRMC_HIGH_METHODCALLS = "fbcontrib.PRMC.highmethodcalls";
     public static final String PRMC_NORMAL_BYTECOUNT = "fbcontrib.PRMC.normalbytecount";
     public static final String PRMC_NORMAL_METHODCALLS = "fbcontrib.PRMC.normalmethodcalls";
 
 	private static Set<StringriskyMethodNameContents = new HashSet<String>();
 	private static int highByteCountLimit = 200;
 	private static int highMethodCallLimit = 10;
 	private static int normalByteCountLimit = 50;
 	private static int normalMethodCallLimit = 4;
 
 	static {
         .add("currentTimeMillis");
         .add("nanoTime");
         .add("newInstance");
         .add("noneOf");
         .add("allOf");
         .add("random");
         .add("beep");
 
 		String userNameProp = System.getProperty();
 		if (userNameProp != null) {
 			String[] userNames = userNameProp.split("\\s*,\\s*");
 			for (String name : userNames) {
 			}
 		}
 		if (prop != null) {
		}
		if (prop != null) {
		}
		if (prop != null) {
		}
		if (prop != null) {
		}
	}
    private static Set<StringriskyClassNames = new HashSet<String>();
    static {
        .add("java/nio/ByteBuffer");
        .add("java/io/DataInputStream");
        .add("java/io/ObjectInputStream");
        String userNameProp = System.getProperty();
        if (userNameProp != null) {
            String[] userNames = userNameProp.split("\\s*,\\s*");
            for (String name : userNames) {
			}
        }
    }
	private final BugReporter bugReporter;
	private OpcodeStack stack = null;
	private Map<IntegerMethodCalllocalMethodCalls = null;
	private Map<StringMethodCallfieldMethodCalls = null;
	private Map<StringMethodCallstaticMethodCalls = null;
	private BitSet branchTargets = null;

constructs a PRMC detector given the reporter to report bugs on

Parameters:
bugReporter the sync of bug reports
		this. = bugReporter;
	}

implements the visitor to create and clear the stack, method call maps, and branch targets

Parameters:
classContext the context object of the currently visited class
	public void visitClassContext(ClassContext classContext) {
		try {
			 = new OpcodeStack();
			super.visitClassContext(classContext);
finally {
			 = null;
			 = null;
		}
	}

implements the visitor to reset the stack, and method call maps for new method

Parameters:
obj the context object of the currently parsed code block
	public void visitCode(Code obj) {
		CodeException[] codeExceptions = obj.getExceptionTable();
		for (CodeException codeEx : codeExceptions) {
			.set(codeEx.getHandlerPC());
		}
		super.visitCode(obj);
	}

implements the visitor to look for repetitive calls to the same method on the same object using the same constant parameters. These methods must return a value.

Parameters:
seen the opcode of the currently parsed instruction
	public void sawOpcode(int seen) {
	    String userValue = null;
		try {
	        .precomputation(this);
	        
            int pc = getPC();
			if (.get(pc)) {
			}
			if (((seen >= IFEQ) && (seen <= GOTO)) || ((seen >= IFNULL) && (seen <= GOTO_W))) {
else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) {
				int[] offsets = getSwitchOffsets();
				for (int offset : offsets) {
					.set(offset + pc);
				}
else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) {
				.remove(Integer.valueOf(RegisterUtils.getAStoreReg(thisseen)));
else if (seen == PUTFIELD) {
			    String fieldSource = "";
			    if (.getStackDepth() > 0) {
			        OpcodeStack.Item item = .getStackItem(0);
			        fieldSource = (Stringitem.getUserValue();
			        if (fieldSource == null)
			            fieldSource = "";
			    }
else if (seen == GETFIELD) {
			    if (.getStackDepth() > 0) {
			        OpcodeStack.Item item = .getStackItem(0);
			        userValue = (Stringitem.getUserValue();
			        if (userValue == null) {
			            int reg = item.getRegisterNumber();
			            if (reg >= 0) {
			                userValue = String.valueOf(reg);
			            } else {
			                XField xf = item.getXField();
			                if (xf != null) {
			                    userValue = xf.getName();
			                }
			            }
			        }
			    }
else if ((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE) || (seen == INVOKESTATIC)) {
				String signature = getSigConstantOperand();
				int parmCount = Type.getArgumentTypes(signature).length;
				int neededStackSize = parmCount - ((seen == INVOKESTATIC) ? 1 : 0);
				if (.getStackDepth() > neededStackSize) {
					Object[] parmConstants = new Object[parmCount];
					for (int i = 0; i < parmCounti++) {
						parmConstants[i] = parm.getConstant();
						if (parmConstants[i] == null) {
							return;
						}
					}
	                String className = getClassConstantOperand();
					int reg = -1;
					XField field = null;
					if (seen == INVOKESTATIC) {
						mc = .get(className);
else {
						OpcodeStack.Item obj = .getStackItem(parmCount);
						reg = obj.getRegisterNumber();
						field = obj.getXField();
						if (reg >= 0) {
							mc = .get(Integer.valueOf(reg));
else if (field != null) {
						    String fieldSource = (Stringobj.getUserValue();
						    if (fieldSource == null)
						        fieldSource = "";
							mc = .get(fieldSource + ":" + field.getName());
else {
							return;
						}
					}
	                String methodName = getNameConstantOperand();
					if (mc != null) {
						if (!signature.endsWith("V") && methodName.equals(mc.getName()) && signature.equals(mc.getSignature()) && !isRiskyName(classNamemethodName)) {
							Object[] parms = mc.getParms();
							if (Arrays.equals(parmsparmConstants)) {
			                    int ln = getLineNumber(pc);
			                    
			                    if ((ln != mc.getLineNumber()) || (Math.abs(pc - mc.getPC()) < 10)) {
    								Statistics statistics = Statistics.getStatistics();
    								MethodInfo mi = statistics.getMethodStatistics(getClassConstantOperand(), methodNamesignature);
    
    								.reportBug(new BugInstance(this"PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS",
    																	  ((mi.getNumBytes() >= ) || (mi.getNumMethodCalls() >= )) ?
    																			  HIGH_PRIORITY :
    																		      ((mi.getNumBytes() >= ) || (mi.getNumMethodCalls() >= )) ?
    																		    		  NORMAL_PRIORITY :
    																		    			  ((mi.getNumBytes() == 0) || (mi.getNumMethodCalls() == 0)) ?
    																		    					  LOW_PRIORITY :
    																		    				      EXP_PRIORITY)
    											.addClass(this)
    											.addMethod(this)
    											.addSourceLine(this)
    											.addString(methodName + signature));
			                    }
							}
						}
						if (seen == INVOKESTATIC) {
else {
							if (reg >= 0) {
else if (field != null) {
				                String fieldSource = "";
				                if (.getStackDepth() > 0) {
				                    OpcodeStack.Item item = .getStackItem(0);
				                    fieldSource = (Stringitem.getUserValue();
				                    if (fieldSource == null)
				                        fieldSource = "";
				                }
								.remove(fieldSource + ":" + field.getName());
							}
						}
else {
					    int ln = getLineNumber(pc);
						if (seen == INVOKESTATIC) {
							.put(classNamenew MethodCall(methodNamesignatureparmConstantspcln));
else {
							if (reg >= 0) {
								.put(Integer.valueOf(reg), new MethodCall(methodNamesignatureparmConstantspcln));
else if (field != null) {
							    OpcodeStack.Item obj = .getStackItem(parmCount);
							    String fieldSource = (Stringobj.getUserValue();
	                            if (fieldSource == null)
	                                fieldSource = "";
								.put(fieldSource + ":" + field.getName(), new MethodCall(methodNamesignatureparmConstantspcln));
							}
						}
					}
				}
			}
finally {
			.sawOpcode(thisseen);
			if (userValue != null) {
			    if (.getStackDepth() > 0) {
			        OpcodeStack.Item item = .getStackItem(0);
			        item.setUserValue(userValue);
			    }
			}
		}
	}

returns true if the class or method name contains a pattern that is considered likely to be this modifying

Parameters:
className the class name to check
methodName the method name to check
Returns:
whether the method sounds like it modifies this
	private boolean isRiskyName(String classNameString methodName) {
        if (.contains(className)) {
			return true;
		}
		for (String riskyName : ) {
			if (methodName.indexOf(riskyName) >= 0) {
				return true;
			}
		}
		return methodName.contains("$");
	}

returns the source line number for the pc, or just the pc if the line number table doesn't exist

Parameters:
pc current pc
Returns:
the line number
	private int getLineNumber(int pc) {
	    LineNumberTable lnt = getMethod().getLineNumberTable();
	    if (lnt == null)
	        return pc;
	    
	    LineNumber[] lns = lnt.getLineNumberTable();
	    if (lns == null)
	        return pc;
	    
	    if (pc > lns[lns.length-1].getStartPC())
	        return lns[lns.length-1].getLineNumber();
	    
	    int lo = 0;
	    int hi = lns.length - 2;
	    int mid = 0;
	    while (lo <= hi) {
	        mid = (lo + hi) / 2;
	        if (pc < lns[mid].getStartPC()) {
	            hi = mid - 1;
	        } else if (pc >= lns[mid+1].getStartPC()) {
	            lo = mid + 1;
	        } else {
	            break;
	        }
	    }
	    
	    return lns[mid].getLineNumber();
	}

contains information about a method call
	static class MethodCall
	{
		private final String methodName;
		private final String methodSignature;
		private final Object[] methodParms;
		private final int methodPC;
		private final int methodLineNumber;
		public MethodCall(String nameString signatureObject[] parmsint pcint lineNumber) {
			 = name;
			 = signature;
			 = parms;
			 = pc;
			 = lineNumber;
		}
		public String getName() {
			return ;
		}
		public String getSignature() {
		}
		public Object[] getParms() {
			return ;
		}
		public int getPC() {
		    return ;
		}
		public int getLineNumber() {
		    return ;
		}
	}
New to GrepCode? Check out our FAQ X