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.CodeException;
 import  org.apache.bcel.classfile.JavaClass;
 import  org.apache.bcel.classfile.LocalVariable;
 import  org.apache.bcel.classfile.LocalVariableTable;
 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.ba.ClassContext;

looks for methods that catch exceptions, and rethrow another exception without encapsulating the original exception within it. Doing this loses the stack history, and where the original problem occurred. This makes finding and fixing errors difficult.
 
 public class LostExceptionStackTrace extends BytecodeScanningDetector
 {
     private static JavaClass errorClass;
     private static JavaClass throwableClass;
     private static JavaClass assertionClass;
 
 	static {
 		try {
              = Repository.lookupClass("java/lang/Error");
              = Repository.lookupClass("java/lang/Throwable");
              = Repository.lookupClass("java/lang/AssertionError");
 		} catch (ClassNotFoundException cnfe) {
              = null;
              = null;
 		}
 	}
 
 	private final BugReporter bugReporter;
 	private OpcodeStack stack;
 	private CodeException[] exceptions;
 	private Set<CatchInfocatchInfos;
 	private Map<IntegerBooleanexReg;
 	private boolean lastWasExitPoint = false;

    
constructs a LEST detector given the reporter to report bugs on

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

implements the visitor to make sure the jdk is 1.4 or better

Parameters:
classContext the context object of the currently parsed class
 
 	public void visitClassContext(ClassContext classContext) {
 		try {
 			if ( != null &&  != null && !isPre14Class(classContext.getJavaClass())) {
 				 = new OpcodeStack();
 				 = new HashMap<IntegerBoolean>();
 				super.visitClassContext(classContext);
			}
finally {
			 = null;
			 = null;
			 = null;
			 = null;
		}
	}

    
looks for methods that contain a catch block and an ATHROW opcode

Parameters:
code the context object of the current code block
method the context object of the current method
Returns:
if the class throws exceptions
	public boolean prescreen(Code code, Method method) {
		if (method.isSynthetic())
        {
            return false;
        }
		CodeException[] ce = code.getExceptionTable();
		if (ce == null || ce.length == 0)
        {
            return false;
        }
		BitSet bytecodeSet = getClassContext().getBytecodeSet(method);
		return bytecodeSet != null && bytecodeSet.get(Constants.ATHROW);
	}

implements the visitor to filter out methods that don't throw exceptions

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

collects all the valid exception objects (ones where start and finish are before the target

Parameters:
exs the exceptions from the class file
Returns:
the filtered exceptions
	public CodeException[] collectExceptions(CodeException[] exs) {
		List<CodeException> filteredEx = new ArrayList<CodeException>();
		for (CodeException ce : exs) {
			if (ce.getCatchType() != 0 && ce.getStartPC() < ce.getEndPC() && ce.getEndPC() <= ce.getHandlerPC()) {
				filteredEx.add(ce);
			}
		}
		return filteredEx.toArray(new CodeException[filteredEx.size()]);
	}

implements the visitor to find throwing alternative exceptions from a catch block, without forwarding along the original exception
	public void sawOpcode(int seen) {
		boolean markAsValid = false;
		try {
			.mergeJumps(this);
			int pc = getPC();
			for (CodeException ex : ) {
				if (pc == ex.getEndPC()) {
					if (seen >= IRETURN && seen <= RETURN)
                    {
                        addCatchBlock(ex.getHandlerPC(), .);
                    }
                    else if (seen == GOTO || seen == GOTO_W)
                    {
                        addCatchBlock(ex.getHandlerPC(), this.getBranchTarget());
                    }
                    else
                    {
                        addCatchBlock(ex.getHandlerPC(), .);
                    }
else if (pc == ex.getHandlerPC()) {
                    removePreviousHandlers(pc);
                }
			}
			while (it.hasNext()) {
				try {
					CatchInfo catchInfo = it.next();
					if (pc == catchInfo.getStart()) {
                        if (!updateExceptionRegister(catchInfoseenpc))
                        {
                            it.remove();
                        }
						break;
else if (pc > catchInfo.getFinish()) {
						it.remove();
						break;
else if (pc > catchInfo.getStart() && pc <= catchInfo.getFinish()) {
						if (seen == INVOKESPECIAL) {
							if ("<init>".equals(getNameConstantOperand())) {
								JavaClass exClass = Repository.lookupClass(className);
								if (exClass.instanceOf() || (exClass.instanceOf())) {
									if (sig.indexOf("Exception") >= 0
									||  sig.indexOf("Throwable") >= 0
									||  sig.indexOf("Error") >= 0) {
										markAsValid = true;
										break;
									}
									if (exClass.instanceOf()) {
	                                    //just ignore LEST for AssertionErrors
	                                    markAsValid = true;
	                                    break;
	                                }
								}
else if (isPossibleExBuilder(catchInfo.getRegister())) {
								markAsValid = true;
							}
else if (seen == INVOKEVIRTUAL) {
                            if ("initCause".equals(getNameConstantOperand())) {
    							String className = getClassConstantOperand();
    							JavaClass exClass = Repository.lookupClass(className);
    							if (exClass.instanceOf()) {
    								if (.getStackDepth() > 1) {
    									OpcodeStack.Item itm = .getStackItem(1);
    									int reg = itm.getRegisterNumber();
    									if (reg >= 0)
                                        {
                                            .put(Integer.valueOf(reg), .);
                                        }
    									markAsValid = true// Fixes javac generated code
    								}
    							}
                            } else if ("getTargetException".equals(getNameConstantOperand()) && "java/lang/reflect/InvocationTargetException".equals(getClassConstantOperand())) {
                                markAsValid = true;
else if (isPossibleExBuilder(catchInfo.getRegister())) {
								markAsValid = true;
							}
else if (seen == INVOKEINTERFACE || seen == INVOKESTATIC) {
                            {
                                markAsValid = true;
                            }
else if (seen == ATHROW) {
							if (.getStackDepth() > 0) {
								OpcodeStack.Item itm = .getStackItem(0);
								if (itm.getRegisterNumber() != catchInfo.getRegister()
								&&  itm.getUserValue() == null) {
                                    if (!isPre14Class(itm.getJavaClass()))
                                    {
                                    	int priority = getPrevOpcode(1) == MONITOREXIT ? LOW_PRIORITY : NORMAL_PRIORITY;
    									.reportBug(new BugInstance(this"LEST_LOST_EXCEPTION_STACK_TRACE"priority)
    													.addClass(this)
    													.addMethod(this)
    													.addSourceLine(this));
                                    }
									it.remove();
									break;
								}
							}
else if (seen == ASTORE || seen >= ASTORE_0 && seen <= ASTORE_3) {
						    if () {
						        //crazy jdk6 finally block injection -- shut off detection
						        .clear();
						        break;
						    }
							if (.getStackDepth() > 0) {
								OpcodeStack.Item itm = .getStackItem(0);
								int reg = RegisterUtils.getAStoreReg(thisseen);
								.put(Integer.valueOf(reg), (Boolean)itm.getUserValue());
								if (reg == catchInfo.getRegister() && catchInfo.getFinish() == .) {
									it.remove();
								}
							}
else if (seen == ALOAD || seen >= ALOAD_0 && seen <= ALOAD_3) {
							Boolean valid = .get(Integer.valueOf(RegisterUtils.getALoadReg(thisseen)));
							if (valid != null)
                            {
                                markAsValid = valid.booleanValue();
                            }
else if (seen >= IRETURN && seen <= RETURN) {
						    break;
						}
					}
catch (ClassNotFoundException cnfe) {
					.reportMissingClass(cnfe);
					it.remove();
				}
			}
			 = seen >= IRETURN && seen <= RETURN || seen == GOTO || seen == GOTO_W || seen == ATHROW;
		}
		finally {
			TernaryPatcher.pre(seen);
			.sawOpcode(thisseen);
			TernaryPatcher.post(seen);
			if (markAsValid) {
				if (.getStackDepth() > 0) {
					OpcodeStack.Item itm = .getStackItem(0);
					itm.setUserValue(.);
				}
			}
		}
	}

returns whether the method called might be a method that builds an exception using the original exception. It does so by looking to see if the method returns an exception, and if one of the parameters is the original exception

Parameters:
excReg the register of the original exception caught
Returns:
whether this method call could be an exception builder method
	public boolean isPossibleExBuilder(int excRegthrows ClassNotFoundException {
		Type returnType = Type.getReturnType(sig);
		String returnSig = returnType.getSignature();
		if (returnSig.startsWith("L")) {
			returnSig = returnSig.substring(1, returnSig.length() - 1);
			JavaClass retCls = Repository.lookupClass(returnSig);
			if (retCls.instanceOf()) {
				int numParms = Type.getArgumentTypes(sig).length;
				if (.getStackDepth() >= numParms) {
					for (int p = 0; p < numParmsp++) {
						OpcodeStack.Item item = .getStackItem(p);
						if (item.getRegisterNumber() == excReg)
                        {
                            return true;
                        }
					}
				}
			}
		}
		return false;
	}

    
returns whether the class in question was compiled with a jdk less than 1.4

Parameters:
cls the class to check
Returns:
whether the class is compiled with a jdk less than 1.4
    private boolean isPre14Class(JavaClass cls)
    {
        return cls != null && cls.getMajor() < Constants.MAJOR_1_4;
    }
    private void removePreviousHandlers(int pc)
    {
        //This unnecessarily squashes some nested catch blocks, but better than false positives
        Iterator<CatchInfoit = .iterator();
        while (it.hasNext()) {
            CatchInfo ci = it.next();
            if (ci.getStart() < pc)
            {
                it.remove();
            }
        }
    }
    private void removeIndeterminateHandlers(int pc)
    {
        Iterator<CatchInfoit = .iterator();
        while (it.hasNext()) {
            CatchInfo ci = it.next();
            if (ci.getStart() < pc && ci.getFinish() == .)
            {
                it.remove();
            }
        }
    }

    
looks to update the catchinfo block with the register used for the exception variable. If their is a local variable table, but the local variable can't be found return false, signifying an empty catch block.

Parameters:
ci the catchinfo record for the catch starting at this pc
seen the opcode of the currently visited instruction
pc the current pc
Returns:
whether the catch block is empty
    private boolean updateExceptionRegister(CatchInfo ciint seenint pc) {
        if (seen == ASTORE || seen >= ASTORE_0 && seen <= ASTORE_3) {
            int reg = RegisterUtils.getAStoreReg(thisseen);
            ci.setReg(reg);
            .put(Integer.valueOf(reg), .);
            LocalVariableTable lvt = getMethod().getLocalVariableTable();
            if (lvt != null) {
                LocalVariable lv = lvt.getLocalVariable(regpc+2);
                if (lv != null) {
                    int finish = lv.getStartPC() + lv.getLength();
                    if (finish < ci.getFinish())
                    {
                        ci.setFinish(finish);
                    }
                }
                else
                {
                    return false;
                }
            }
        }
        return true;
    }

    
add a catch block info record for the catch block that is guessed to be in the range of start to finish

Parameters:
start the handler pc
finish the guessed end of the catch block
	private void addCatchBlock(int startint finish) {
		CatchInfo ci = new CatchInfo(startfinish);
	}
	private static class CatchInfo {
		private final int catchStart;
		private int catchFinish;
		private int exReg;
		public CatchInfo(int startint finish) {
			 = start;
			 = finish;
			 = -1;
		}
		public void setReg(int reg) {
			 = reg;
		}
		public int getStart() {
			return ;
		}
		public int getFinish() {
			return ;
		}
		public void setFinish(int finish) {
			 = finish;
		}
		public int getRegister() {
			return ;
		}
	}
New to GrepCode? Check out our FAQ X