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.Set;
 
 import  org.apache.bcel.Repository;
 import  org.apache.bcel.classfile.Code;
 import  org.apache.bcel.classfile.Constant;
 import  org.apache.bcel.classfile.ConstantClass;
 import  org.apache.bcel.classfile.ConstantPool;
 import  org.apache.bcel.classfile.ConstantUtf8;
 import  org.apache.bcel.classfile.JavaClass;
 import  org.apache.bcel.classfile.Method;
 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.OpcodeStack;
 import  edu.umd.cs.findbugs.OpcodeStack.CustomUserValue;
 import  edu.umd.cs.findbugs.ba.ClassContext;
 
 @CustomUserValue
 public class LoggerOddities extends BytecodeScanningDetector {
     private static JavaClass THROWABLE_CLASS;
     private static Set<StringLOGGER_METHODS;
     private static Pattern BAD_FORMATTING_ANCHOR = Pattern.compile("\\{[0-9]\\}");
     private static Pattern FORMATTER_ANCHOR = Pattern.compile("\\{\\}");
 
     static {
         try {
              = Repository.lookupClass("java/lang/Throwable");
 
              = new HashSet<String>();
             .add("trace");
             .add("debug");
             .add("info");
             .add("warn");
             .add("error");
             .add("fatal");
         } catch (ClassNotFoundException cnfe) {
              = null;
         }
     }
     private final BugReporter bugReporter;
     private OpcodeStack stack;
     private String clsName;

    
constructs a LO detector given the reporter to report bugs on.

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

    
implements the visitor to discover what the class name is if it is a normal class, or the owning class, if the class is an anonymous class.

Parameters:
classContext the context object of the currently parsed class
 
     @Override
     public void visitClassContext(ClassContext classContext) {
         try {
              = new OpcodeStack();
              = classContext.getJavaClass().getClassName();
             int subclassIndex = .indexOf('$');
             while (subclassIndex >= 0) {
                 String simpleName = .substring(subclassIndex + 1);
                 try {
                     Integer.parseInt(simpleName);
                      = .substring(0, subclassIndex);
                     subclassIndex = .indexOf('$');
                } catch (NumberFormatException nfe) {
                    subclassIndex = -1;
                }
            }
             = .replace('.''/');
            super.visitClassContext(classContext);
        } finally {
             = null;
        }
    }

    
implements the visitor to reset the stack

Parameters:
obj the context object of the currently parsed code block
    @Override
    public void visitCode(Code obj) {
        .resetForMethodEntry(this);
        Method m = getMethod();
        if ("<init>".equals(m.getName())) {
            Type[] types = Type.getArgumentTypes(m.getSignature());
            for (Type t : types) {
                String parmSig = t.getSignature();
                if ("Lorg/slf4j/Logger;".equals(parmSig) || "Lorg/apache/log4j/Logger;".equals(parmSig) || "Lorg/apache/commons/logging/Log;".equals(parmSig)) {
                    .reportBug(new BugInstance(this"LO_SUSPECT_LOG_PARAMETER", NORMAL_PRIORITY).addClass(this).addMethod(this)
                            .addSourceLine(this));
                }
            }
        }
        super.visitCode(obj);
    }

    
implements the visitor to look for calls to Logger.getLogger with the wrong class name

Parameters:
seen the opcode of the currently parsed instruction
    @Override
    public void sawOpcode(int seen) {
        String ldcClassName = null;
        int exMessageReg = -1;
        Integer arraySize = null;
        try {
            .precomputation(this);
            
            if ((seen == LDC) || (seen == LDC_W)) {
                Constant c = getConstantRefOperand();
                if (c instanceof ConstantClass) {
                    ConstantPool pool = getConstantPool();
                    ldcClassName = ((ConstantUtf8) pool.getConstant(((ConstantClass) c).getNameIndex())).getBytes();
                }
            } else if (seen == INVOKESTATIC) {
                String callingClsName = getClassConstantOperand();
                String mthName = getNameConstantOperand();
                String loggingClassName = null;
                if ("org/slf4j/LoggerFactory".equals(callingClsName) && "getLogger".equals(mthName)) {
                    String signature = getSigConstantOperand();
                    if ("(Ljava/lang/Class;)Lorg/slf4j/Logger;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getUserValue();
                        }
                    } else if ("(Ljava/lang/String;)Lorg/slf4j/Logger;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getConstant();
                            if (loggingClassName != null) {
                                loggingClassName = loggingClassName.replace('.''/');
                            }
                        }
                    }
                } else if ("org/apache/log4j/Logger".equals(callingClsName) && "getLogger".equals(mthName)) {
                    String signature = getSigConstantOperand();
                    if ("(Ljava/lang/Class;)Lorg/apache/log4j/Logger;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getUserValue();
                        }
                    } else if ("(Ljava/lang/String;)Lorg/apache/log4j/Logger;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getConstant();
                            if (loggingClassName != null) {
                                loggingClassName = loggingClassName.replace('.''/');
                            }
                        }
                    } else if ("(Ljava/lang/String;Lorg/apache/log4j/spi/LoggerFactory;)Lorg/apache/log4j/Logger;".equals(signature)) {
                        if (.getStackDepth() > 1) {
                            OpcodeStack.Item item = .getStackItem(1);
                            loggingClassName = (Stringitem.getConstant();
                            if (loggingClassName != null) {
                                loggingClassName = loggingClassName.replace('.''/');
                            }
                        }
                    }
                } else if ("org/apache/commons/logging/LogFactory".equals(callingClsName) && "getLog".equals(mthName)) {
                    String signature = getSigConstantOperand();
                    if ("(Ljava/lang/Class;)Lorg/apache/commons/logging/Log;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getUserValue();
                        }
                    } else if ("(Ljava/lang/String;)Lorg/apache/commons/logging/Log;".equals(signature)) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item item = .getStackItem(0);
                            loggingClassName = (Stringitem.getConstant();
                            if (loggingClassName != null) {
                                loggingClassName = loggingClassName.replace('.''/');
                            }
                        }
                    }
                }
                if (loggingClassName != null) {
                    if (.getStackDepth() > 0) {
                        if (!loggingClassName.equals()) {
                            boolean isPrefix = .startsWith(loggingClassName);
                            boolean isAnonClassPrefix;
                            if (isPrefix) {
                                String anonClass = .substring(loggingClassName.length());
                                isAnonClassPrefix = anonClass.matches("(\\$\\d+)+");
                            } else {
                                isAnonClassPrefix = false;
                            }
                            if (!isAnonClassPrefix) {
                                .reportBug(new BugInstance(this"LO_SUSPECT_LOG_CLASS", NORMAL_PRIORITY).addClass(this).addMethod(this)
                                        .addSourceLine(this));
                            }
                        }
                    }
                }
            } else if (((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) && ( != null)) {
                String mthName = getNameConstantOperand();
                if (mthName.equals("getMessage")) {
                    String callingClsName = getClassConstantOperand();
                    JavaClass cls = Repository.lookupClass(callingClsName);
                    if (cls.instanceOf()) {
                        if (.getStackDepth() > 0) {
                            OpcodeStack.Item exItem = .getStackItem(0);
                            exMessageReg = exItem.getRegisterNumber();
                        }
                    }
                } else if (.contains(mthName)) {
                    String callingClsName = getClassConstantOperand();
                    if (callingClsName.endsWith("Log") || (callingClsName.endsWith("Logger"))) {
                        String sig = getSigConstantOperand();
                        if ("(Ljava/lang/String;Ljava/lang/Throwable;)V".equals(sig) || "(Ljava/lang/Object;Ljava/lang/Throwable;)V".equals(sig)) {
                            if (.getStackDepth() >= 2) {
                                OpcodeStack.Item exItem = .getStackItem(0);
                                OpcodeStack.Item msgItem = .getStackItem(1);
                                Integer exReg = (IntegermsgItem.getUserValue();
                                if (exReg != null) {
                                    if (exReg.intValue() == exItem.getRegisterNumber()) {
                                        .reportBug(new BugInstance(this"LO_STUTTERED_MESSAGE", NORMAL_PRIORITY).addClass(this).addMethod(this)
                                                .addSourceLine(this));
                                    }
                                }
                            }
                        } else if ("(Ljava/lang/Object;)V".equals(sig)) {
                            if (.getStackDepth() > 0) {
                                final JavaClass clazz = .getStackItem(0).getJavaClass();
                                if ((clazz != null) && clazz.instanceOf()) {
                                    .reportBug(new BugInstance(this"LO_LOGGER_LOST_EXCEPTION_STACK_TRACE", NORMAL_PRIORITY).addClass(this)
                                            .addMethod(this).addSourceLine(this));
                                }
                            }
                        } else if ("org/slf4j/Logger".equals(callingClsName)) {
                            String signature = getSigConstantOperand();
                            if ("(Ljava/lang/String;Ljava/lang/Object;)V".equals(signature
                            ||  "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V".equals(signature
                            ||  "(Ljava/lang/String;[Ljava/lang/Object;)V".equals(signature)) {
                                int numParms = Type.getArgumentTypes(signature).length;
                                if (.getStackDepth() >= numParms) {
                                    OpcodeStack.Item formatItem = .getStackItem(numParms - 1);
                                    Object con = formatItem.getConstant();
                                    if (con instanceof String) {
                                        Matcher m = .matcher((Stringcon);
                                        if (m.find()) {
                                            .reportBug(new BugInstance(this"LO_INVALID_FORMATTING_ANCHOR", NORMAL_PRIORITY)
                                                        .addClass(this)
                                                        .addMethod(this)
                                                        .addSourceLine(this));
                                        } else {
                                            int actualParms = getSLF4JParmCount(signature);
                                            if (actualParms != -1) {
                                                int expectedParms = countAnchors((Stringcon);
                                                boolean hasEx = hasExceptionOnStack();
                                                if ((!hasEx && (expectedParms != actualParms)) || (hasEx && ((expectedParms != (actualParms - 1)) && (expectedParms != actualParms)))) {
                                                    .reportBug(new BugInstance(this"LO_INCORRECT_NUMBER_OF_ANCHOR_PARAMETERS", NORMAL_PRIORITY)
                                                            .addClass(this)
                                                            .addMethod(this)
                                                            .addSourceLine(this)
                                                            .addString("Expected: " + expectedParms)
                                                            .addString("Actual: " + actualParms));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            
                        }
                    }
                }
            } else if (seen == INVOKESPECIAL) {
                if ("<init>".equals(getNameConstantOperand())) {         
                    String cls = getClassConstantOperand();
                    if ((cls.startsWith("java/") || cls.startsWith("javax/")) && cls.endsWith("Exception")) {
                        String sig = getSigConstantOperand();
                        Type[] types = Type.getArgumentTypes(sig);
                        if (types.length <= .getStackDepth()) {
                            for (int i = 0; i < types.length; i++) {
                                if ("Ljava/lang/String;".equals(types[i].getSignature())) {
                                    OpcodeStack.Item item = .getStackItem(types.length - i - 1);
                                    String cons = (Stringitem.getConstant();
                                    if ((cons != null) && cons.contains("{}")) {
                                        .reportBug(new BugInstance(this"LO_EXCEPTION_WITH_LOGGER_PARMS", NORMAL_PRIORITY)
                                                    .addClass(this)
                                                    .addMethod(this)
                                                    .addSourceLine(this));
                                        break;
                                    }
                                }
                            }
                        }    
                    }
                }
            } else if (seen == ANEWARRAY) {
                if (.getStackDepth() > 0) {
                    OpcodeStack.Item sizeItem = .getStackItem(0);
                    Object con = sizeItem.getConstant();
                    if (con instanceof Integer) {
                        arraySize = (Integercon;
                    }
                }
            } else if (seen == AASTORE) {
                if (.getStackDepth() >= 3) {
                    OpcodeStack.Item arrayItem = .getStackItem(2);
                    Integer size = (IntegerarrayItem.getUserValue();
                    if ((size != null) && (size.intValue() > 0)) {
                        if (hasExceptionOnStack()) {
                            Integer sz = Integer.valueOf(-size.intValue());
                            arrayItem.setUserValue(sz);
                        }
                    }
                }
            }
        } catch (ClassNotFoundException cnfe) {
            .reportMissingClass(cnfe);
        } finally {
            TernaryPatcher.pre(seen);
            .sawOpcode(thisseen);
            TernaryPatcher.post(seen);
            if (ldcClassName != null) {
                if (.getStackDepth() > 0) {
                    OpcodeStack.Item item = .getStackItem(0);
                    item.setUserValue(ldcClassName);
                }
            }
            if (exMessageReg >= 0) {
                if (.getStackDepth() > 0) {
                    OpcodeStack.Item item = .getStackItem(0);
                    item.setUserValue(Integer.valueOf(exMessageReg));
                }
            }
            if (arraySize != null) {
                if (.getStackDepth() > 0) {
                    OpcodeStack.Item item = .getStackItem(0);
                    item.setUserValue(arraySize);
                }
            }
        }
    }
    
    
returns the number of anchors {} in a string

Parameters:
formatString the format string
Returns:
the number of anchors
    private int countAnchors(String formatString) {
        Matcher m = .matcher(formatString);
        int count = 0;
        int start = 0;
        while (m.find(start)) {
            ++count;
            start = m.end();
        }
        
        return count;
    }
    
    
returns the number of parameters slf4j is expecting to inject into the format string

Parameters:
signature the method signature of the error, warn, info, debug statement
Returns:
the number of expected parameters
    private int getSLF4JParmCount(String signature) {
        if ("(Ljava/lang/String;Ljava/lang/Object;)V".equals(signature))
            return 1;
        if ("(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V".equals(signature))
            return 2;
        
        OpcodeStack.Item item = .getStackItem(0);
        Integer size = (Integeritem.getUserValue();
        if (size != null) {
            return Math.abs(size.intValue());
        }
        return -1; 
    }
    
    
returns whether an exception object is on the stack slf4j will find this, and not include it in the parm list so i we find one, just don't report

Returns:
whether or not an exception i present
    private boolean hasExceptionOnStack() {
        try {
            for (int i = 0; i < .getStackDepth() - 1; i++) {
                OpcodeStack.Item item = .getStackItem(i);
                String sig = item.getSignature();
                if (sig.startsWith("L")) {
                    String name = sig.substring(1, sig.length() - 1);
                    JavaClass cls = Repository.lookupClass(name);
                    if (cls.instanceOf())
                        return true;
                } else if (sig.startsWith("[")) {
                    Integer sz = (Integeritem.getUserValue();
                    if ((sz != null) && (sz.intValue() < 0))
                        return true;
                }
            }
            return false;
        } catch (ClassNotFoundException cnfe) {
            .reportMissingClass(cnfe);
            return true;
        }
    }
New to GrepCode? Check out our FAQ X