001: /* Soot - a J*va Optimization Framework
002: * Copyright (C) 2004 Jennifer Lhotak
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the
016: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
017: * Boston, MA 02111-1307, USA.
018: */
019:
020: package soot.jimple.toolkits.base;
021:
022: import soot.*;
023: import soot.jimple.*;
024: import soot.util.*;
025: import java.util.*;
026: import soot.tagkit.*;
027:
028: public class ExceptionChecker extends BodyTransformer {
029:
030: FastHierarchy hierarchy;
031: ExceptionCheckerErrorReporter reporter;
032:
033: public ExceptionChecker(ExceptionCheckerErrorReporter r) {
034: this .reporter = r;
035: }
036:
037: protected void internalTransform(Body b, String phaseName,
038: Map options) {
039:
040: Iterator it = b.getUnits().iterator();
041: while (it.hasNext()) {
042: Stmt s = (Stmt) it.next();
043: if (s instanceof ThrowStmt) {
044: ThrowStmt ts = (ThrowStmt) s;
045: checkThrow(b, ts);
046: } else if (s instanceof InvokeStmt) {
047: InvokeStmt is = (InvokeStmt) s;
048: checkInvoke(b, is);
049: } else if ((s instanceof AssignStmt)
050: && (((AssignStmt) s).getRightOp() instanceof InvokeExpr)) {
051: InvokeExpr ie = (InvokeExpr) ((AssignStmt) s)
052: .getRightOp();
053: checkInvokeExpr(b, ie, s);
054: }
055: }
056: }
057:
058: protected void checkThrow(Body b, ThrowStmt ts) {
059: if (isThrowDeclared(b, ((RefType) ts.getOp().getType())
060: .getSootClass())
061: || isThrowFromCompiler(ts)
062: || isExceptionCaught(b, ts, (RefType) ts.getOp()
063: .getType()))
064: return;
065: if (reporter != null) {
066: reporter.reportError(new ExceptionCheckerError(b
067: .getMethod(), ((RefType) ts.getOp().getType())
068: .getSootClass(), ts, (SourceLnPosTag) ts.getOpBox()
069: .getTag("SourceLnPosTag")));
070: }
071: }
072:
073: // does the method declare the throw if its a throw that needs declaring
074: // RuntimeException and subclasses do not need to be declared
075: // Error and subclasses do not need to be declared
076: protected boolean isThrowDeclared(Body b, SootClass throwClass) {
077: if (hierarchy == null) {
078: hierarchy = new FastHierarchy();
079: }
080:
081: // handles case when exception is RuntimeException or Error
082: if (throwClass.equals(Scene.v().getSootClass(
083: "java.lang.RuntimeException"))
084: || throwClass.equals(Scene.v().getSootClass(
085: "java.lang.Error")))
086: return true;
087: // handles case when exception is a subclass of RuntimeException or Error
088: if (hierarchy.isSubclass(throwClass, Scene.v().getSootClass(
089: "java.lang.RuntimeException"))
090: || hierarchy.isSubclass(throwClass, Scene.v()
091: .getSootClass("java.lang.Error")))
092: return true;
093:
094: // handles case when exact exception is thrown
095: if (b.getMethod().throwsException(throwClass))
096: return true;
097:
098: // handles case when a super type of the exception is thrown
099: Iterator<SootClass> it = b.getMethod().getExceptions()
100: .iterator();
101: while (it.hasNext()) {
102: SootClass nextEx = it.next();
103: if (hierarchy.isSubclass(throwClass, nextEx))
104: return true;
105: }
106: return false;
107: }
108:
109: // is the throw created by the compiler
110: protected boolean isThrowFromCompiler(ThrowStmt ts) {
111: if (ts.hasTag("ThrowCreatedByCompilerTag"))
112: return true;
113: return false;
114: }
115:
116: // is the throw caught inside the method
117: protected boolean isExceptionCaught(Body b, Stmt s,
118: RefType throwType) {
119: if (hierarchy == null) {
120: hierarchy = new FastHierarchy();
121: }
122: Iterator it = b.getTraps().iterator();
123: while (it.hasNext()) {
124: Trap trap = (Trap) it.next();
125: if (trap.getException().getType().equals(throwType)
126: || hierarchy.isSubclass(throwType.getSootClass(),
127: (trap.getException().getType())
128: .getSootClass())) {
129: if (isThrowInStmtRange(b, (Stmt) trap.getBeginUnit(),
130: (Stmt) trap.getEndUnit(), s))
131: return true;
132: }
133: }
134: return false;
135: }
136:
137: protected boolean isThrowInStmtRange(Body b, Stmt begin, Stmt end,
138: Stmt s) {
139: Iterator it = b.getUnits().iterator(begin, end);
140: while (it.hasNext()) {
141: if (it.next().equals(s))
142: return true;
143: }
144: return false;
145: }
146:
147: protected void checkInvoke(Body b, InvokeStmt is) {
148: checkInvokeExpr(b, is.getInvokeExpr(), is);
149: }
150:
151: // Given a method signature, see if it is declared in the given interface.
152: // If so, return the exceptions thrown by the declaration. Otherwise,
153: // Do the same thing recursively on superinterfaces and Object
154: // and return the intersection. This gives
155: // the maximal set of exceptions that could be declared to be thrown if the
156: // interface had declared the method. Returns null if no supertype declares
157: // the method.
158: private List<SootClass> getExceptionSpec(SootClass intrface,
159: NumberedString sig) {
160: if (intrface.declaresMethod(sig))
161: return intrface.getMethod(sig).getExceptions();
162: List<SootClass> result = null;
163: SootClass obj = Scene.v().getSootClass("java.lang.Object");
164: if (obj.declaresMethod(sig))
165: result = new Vector<SootClass>(obj.getMethod(sig)
166: .getExceptions());
167: Iterator intrfacesit = intrface.getInterfaces().iterator();
168: while (intrfacesit.hasNext()) {
169: SootClass suprintr = (SootClass) intrfacesit.next();
170: List<SootClass> other = getExceptionSpec(suprintr, sig);
171: if (other != null)
172: if (result == null)
173: result = other;
174: else
175: result.retainAll(other);
176: }
177: return result;
178: }
179:
180: protected void checkInvokeExpr(Body b, InvokeExpr ie, Stmt s) {
181: if (ie instanceof InstanceInvokeExpr
182: && ((InstanceInvokeExpr) ie).getBase().getType() instanceof ArrayType
183: && ie.getMethodRef().name().equals("clone")
184: && ie.getMethodRef().parameterTypes().size() == 0)
185: return; // the call is to the clone() method of an array type, which
186: // is defined not to throw any exceptions; if we left this to
187: // normal resolution we'd get the method in Object which does
188: // throw CloneNotSupportedException
189:
190: List exceptions = ie instanceof InterfaceInvokeExpr
191: // For an invokeinterface, there is no unique resolution for the
192: // method reference that will get the "correct" exception spec. We
193: // actually need to look at the intersection of all declarations of
194: // the method in supertypes.
195: ? getExceptionSpec(ie.getMethodRef().declaringClass(), ie
196: .getMethodRef().getSubSignature())
197: // Otherwise, we just do normal resolution.
198: : ie.getMethod().getExceptions();
199: Iterator it = exceptions.iterator();
200: while (it.hasNext()) {
201: SootClass sc = (SootClass) it.next();
202: if (isThrowDeclared(b, sc)
203: || isExceptionCaught(b, s, sc.getType()))
204: continue;
205: if (reporter != null) {
206: if (s instanceof InvokeStmt) {
207: reporter.reportError(new ExceptionCheckerError(b
208: .getMethod(), sc, s, (SourceLnPosTag) s
209: .getTag("SourceLnPosTag")));
210: } else if (s instanceof AssignStmt) {
211: reporter.reportError(new ExceptionCheckerError(b
212: .getMethod(), sc, s,
213: (SourceLnPosTag) ((AssignStmt) s)
214: .getRightOpBox().getTag(
215: "SourceLnPosTag")));
216: }
217: }
218: }
219: }
220: }
|