/* Soot - a J*va Optimization Framework * Copyright (C) 2004 Jennifer Lhotak * * 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 soot.jimple.toolkits.base; import soot.*; import soot.jimple.*; import soot.util.*; import java.util.*; import soot.tagkit.*; public class ExceptionChecker extends BodyTransformer{ FastHierarchy hierarchy; ExceptionCheckerErrorReporter reporter; public ExceptionChecker(ExceptionCheckerErrorReporter r){ this.reporter = r; } protected void internalTransform(Body b, String phaseName, Map options){ Iterator it = b.getUnits().iterator(); while (it.hasNext()){ Stmt s = (Stmt)it.next(); if (s instanceof ThrowStmt){ ThrowStmt ts = (ThrowStmt)s; checkThrow(b, ts); } else if (s instanceof InvokeStmt){ InvokeStmt is = (InvokeStmt)s; checkInvoke(b, is); } else if ((s instanceof AssignStmt) && (((AssignStmt)s).getRightOp() instanceof InvokeExpr)){ InvokeExpr ie = (InvokeExpr)((AssignStmt)s).getRightOp(); checkInvokeExpr(b, ie, s); } } } protected void checkThrow(Body b, ThrowStmt ts){ if (isThrowDeclared(b, ((RefType)ts.getOp().getType()).getSootClass()) || isThrowFromCompiler(ts) || isExceptionCaught(b, ts, (RefType)ts.getOp().getType())) return; if (reporter != null){ reporter.reportError(new ExceptionCheckerError(b.getMethod(), ((RefType)ts.getOp().getType()).getSootClass(), ts, (SourceLnPosTag)ts.getOpBox().getTag("SourceLnPosTag"))); } } // does the method declare the throw if its a throw that needs declaring // RuntimeException and subclasses do not need to be declared // Error and subclasses do not need to be declared protected boolean isThrowDeclared(Body b, SootClass throwClass){ if (hierarchy == null){ hierarchy = new FastHierarchy(); } // handles case when exception is RuntimeException or Error if (throwClass.equals(Scene.v().getSootClass("java.lang.RuntimeException")) || throwClass.equals(Scene.v().getSootClass("java.lang.Error"))) return true; // handles case when exception is a subclass of RuntimeException or Error if (hierarchy.isSubclass(throwClass, Scene.v().getSootClass("java.lang.RuntimeException")) || hierarchy.isSubclass(throwClass, Scene.v().getSootClass("java.lang.Error"))) return true; // handles case when exact exception is thrown if (b.getMethod().throwsException(throwClass)) return true; // handles case when a super type of the exception is thrown Iterator<SootClass> it = b.getMethod().getExceptions().iterator(); while (it.hasNext()){ SootClass nextEx = it.next(); if (hierarchy.isSubclass(throwClass, nextEx)) return true; } return false; } // is the throw created by the compiler protected boolean isThrowFromCompiler(ThrowStmt ts){ if (ts.hasTag("ThrowCreatedByCompilerTag")) return true; return false; } // is the throw caught inside the method protected boolean isExceptionCaught(Body b, Stmt s, RefType throwType){ if (hierarchy == null){ hierarchy = new FastHierarchy(); } Iterator it = b.getTraps().iterator(); while (it.hasNext()){ Trap trap = (Trap)it.next(); if (trap.getException().getType().equals(throwType) || hierarchy.isSubclass(throwType.getSootClass(), (trap.getException().getType()).getSootClass())){ if (isThrowInStmtRange(b, (Stmt)trap.getBeginUnit(), (Stmt)trap.getEndUnit(), s)) return true; } } return false; } protected boolean isThrowInStmtRange(Body b, Stmt begin, Stmt end, Stmt s){ Iterator it = b.getUnits().iterator(begin, end); while (it.hasNext()){ if (it.next().equals(s)) return true; } return false; } protected void checkInvoke(Body b, InvokeStmt is){ checkInvokeExpr(b, is.getInvokeExpr(), is); } // Given a method signature, see if it is declared in the given interface. // If so, return the exceptions thrown by the declaration. Otherwise, // Do the same thing recursively on superinterfaces and Object // and return the intersection. This gives // the maximal set of exceptions that could be declared to be thrown if the // interface had declared the method. Returns null if no supertype declares // the method. private List<SootClass> getExceptionSpec(SootClass intrface,NumberedString sig) { if(intrface.declaresMethod(sig)) return intrface.getMethod(sig).getExceptions(); List<SootClass> result=null; SootClass obj=Scene.v().getSootClass("java.lang.Object"); if(obj.declaresMethod(sig)) result=new Vector<SootClass>(obj.getMethod(sig).getExceptions()); Iterator intrfacesit=intrface.getInterfaces().iterator(); while(intrfacesit.hasNext()) { SootClass suprintr=(SootClass) intrfacesit.next(); List<SootClass> other=getExceptionSpec(suprintr,sig); if(other!=null) if(result==null) result=other; else result.retainAll(other); } return result; } protected void checkInvokeExpr(Body b, InvokeExpr ie, Stmt s){ if(ie instanceof InstanceInvokeExpr && ((InstanceInvokeExpr) ie).getBase().getType() instanceof ArrayType && ie.getMethodRef().name().equals("clone") && ie.getMethodRef().parameterTypes().size()==0) return; // the call is to the clone() method of an array type, which // is defined not to throw any exceptions; if we left this to // normal resolution we'd get the method in Object which does // throw CloneNotSupportedException List exceptions=ie instanceof InterfaceInvokeExpr // For an invokeinterface, there is no unique resolution for the // method reference that will get the "correct" exception spec. We // actually need to look at the intersection of all declarations of // the method in supertypes. ? getExceptionSpec(ie.getMethodRef().declaringClass(), ie.getMethodRef().getSubSignature()) // Otherwise, we just do normal resolution. : ie.getMethod().getExceptions(); Iterator it = exceptions.iterator(); while (it.hasNext()){ SootClass sc = (SootClass)it.next(); if (isThrowDeclared(b, sc) || isExceptionCaught(b, s, sc.getType())) continue; if (reporter != null){ if (s instanceof InvokeStmt){ reporter.reportError(new ExceptionCheckerError(b.getMethod(), sc, s, (SourceLnPosTag)s.getTag("SourceLnPosTag"))); } else if (s instanceof AssignStmt){ reporter.reportError(new ExceptionCheckerError(b.getMethod(), sc, s, (SourceLnPosTag)((AssignStmt)s).getRightOpBox().getTag("SourceLnPosTag"))); } } } } }