001 /* Copyright (c) 2005-2008, Torbjorn Ekman 002 * All rights reserved. 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * 1. Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 010 * 2. Redistributions in binary form must reproduce the above copyright notice, 011 * this list of conditions and the following disclaimer in the documentation 012 * and/or other materials provided with the distribution. 013 * 014 * 3. Neither the name of the copyright holder nor the names of its 015 * contributors may be used to endorse or promote products derived from this 016 * software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 * POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031 aspect ExceptionHandling { 032 033 /** 034 * @return <code>true</code> if this TyStmt has a non-empty finally block 035 */ 036 syn lazy boolean TryStmt.hasNonEmptyFinally() = 037 hasFinally() && getFinally().getNumStmt() > 0; 038 039 // exception types 040 eq Program.getChild().typeException() = lookupType("java.lang", "Exception"); 041 inh lazy TypeDecl TypeDecl.typeException(); 042 eq Program.getChild().typeRuntimeException() = lookupType("java.lang", "RuntimeException"); 043 inh lazy TypeDecl TypeDecl.typeRuntimeException(); 044 eq Program.getChild().typeError() = lookupType("java.lang", "Error"); 045 inh lazy TypeDecl TypeDecl.typeError(); 046 eq Program.getChild().typeNullPointerException() = lookupType("java.lang", "NullPointerException"); 047 inh lazy TypeDecl ThrowStmt.typeNullPointerException(); 048 eq Program.getChild().typeThrowable() = lookupType("java.lang", "Throwable"); 049 inh lazy TypeDecl BodyDecl.typeThrowable(); 050 051 syn lazy boolean TypeDecl.isException() = instanceOf(typeException()); 052 053 /** 054 * Checked exceptions must be declared thrown or caught in 055 * an enclosing try-statement. 056 * 057 * Note that this attribute is the opposite of isUncheckedException, i.e. 058 * the type is not tested for being a subclass of java.lang.Exception. 059 * 060 * @return {@code true} if this type is not a subtype of java.lang.RuntimException 061 * or java.lang.Error 062 */ 063 syn lazy boolean TypeDecl.isCheckedException() = 064 !(instanceOf(typeRuntimeException()) || instanceOf(typeError())); 065 066 /** 067 * Unchecked exceptions need not be declared thrown or caught in 068 * an enclosing try-statement. 069 * 070 * @return {@code true} if this type is a subtype of java.lang.RuntimException 071 * or java.lang.Error 072 */ 073 syn lazy boolean TypeDecl.isUncheckedException() = 074 instanceOf(typeRuntimeException()) || instanceOf(typeError()); 075 076 inh boolean MethodAccess.handlesException(TypeDecl exceptionType); 077 inh boolean ConstructorAccess.handlesException(TypeDecl exceptionType); 078 inh lazy boolean ThrowStmt.handlesException(TypeDecl exceptionType); 079 inh lazy boolean InstanceInitializer.handlesException(TypeDecl exceptionType); 080 inh lazy boolean StaticInitializer.handlesException(TypeDecl exceptionType); 081 inh boolean FieldDeclaration.handlesException(TypeDecl exceptionType); 082 inh lazy boolean TryStmt.handlesException(TypeDecl exceptionType); 083 inh lazy boolean ConstructorDecl.handlesException(TypeDecl exceptionType); 084 inh lazy boolean MethodDecl.handlesException(TypeDecl exceptionType); 085 inh boolean ClassInstanceExpr.handlesException(TypeDecl exceptionType); 086 087 public void ASTNode.exceptionHandling() { 088 } 089 090 public void MethodAccess.exceptionHandling() { 091 for (Iterator iter = exceptionCollection().iterator(); iter.hasNext(); ) { 092 TypeDecl exceptionType = (TypeDecl) iter.next(); 093 if (exceptionType.isCheckedException() && !handlesException(exceptionType)) { 094 errorf("%s.%s invoked in %s may throw uncaught exception %s", 095 decl().hostType().fullName(), this.name(), 096 hostType().fullName(), exceptionType.fullName()); 097 } 098 } 099 } 100 101 syn lazy Collection MethodAccess.exceptionCollection() { 102 //System.out.println("Computing exceptionCollection for " + name()); 103 HashSet set = new HashSet(); 104 Iterator iter = decls().iterator(); 105 if (!iter.hasNext()) { 106 return set; 107 } 108 109 MethodDecl m = (MethodDecl) iter.next(); 110 //System.out.println("Processing first found method " + m.signature() + " in " + m.hostType().fullName()); 111 112 for (int i = 0; i < m.getNumException(); i++) { 113 TypeDecl exceptionType = m.getException(i).type(); 114 set.add(exceptionType); 115 } 116 while (iter.hasNext()) { 117 HashSet first = new HashSet(); 118 first.addAll(set); 119 HashSet second = new HashSet(); 120 m = (MethodDecl) iter.next(); 121 //System.out.println("Processing the next method " + m.signature() + " in " + m.hostType().fullName()); 122 for (int i = 0; i < m.getNumException(); i++) { 123 TypeDecl exceptionType = m.getException(i).type(); 124 second.add(exceptionType); 125 } 126 set = new HashSet(); 127 for (Iterator i1 = first.iterator(); i1.hasNext(); ) { 128 TypeDecl firstType = (TypeDecl) i1.next(); 129 for (Iterator i2 = second.iterator(); i2.hasNext(); ) { 130 TypeDecl secondType = (TypeDecl) i2.next(); 131 if (firstType.instanceOf(secondType)) { 132 set.add(firstType); 133 } else if (secondType.instanceOf(firstType)) { 134 set.add(secondType); 135 } 136 } 137 } 138 } 139 return set; 140 } 141 142 public void ConstructorAccess.exceptionHandling() { 143 for (Access exception : decl().getExceptionList()) { 144 TypeDecl exceptionType = exception.type(); 145 if (exceptionType.isCheckedException() && !handlesException(exceptionType)) { 146 errorf("%s may throw uncaught exception %s", 147 this.prettyPrint(), exceptionType.fullName()); 148 } 149 } 150 } 151 152 public void ClassInstanceExpr.exceptionHandling() { 153 for (Access exception : decl().getExceptionList()) { 154 TypeDecl exceptionType = exception.type(); 155 if (exceptionType.isCheckedException() && !handlesException(exceptionType)) { 156 errorf("%s may throw uncaught exception %s; it must be caught or declared as being thrown", 157 this.prettyPrint(), exceptionType.fullName()); 158 } 159 } 160 } 161 162 public void ThrowStmt.exceptionHandling() { 163 TypeDecl exceptionType = getExpr().type(); 164 if (exceptionType == typeNull()) { 165 exceptionType = typeNullPointerException(); 166 } 167 // 8.4.4 168 if (exceptionType.isCheckedException() && !handlesException(exceptionType)) { 169 errorf("%s throws uncaught exception %s", this.prettyPrint(), exceptionType.fullName()); 170 } 171 } 172 173 eq Program.getChild().handlesException(TypeDecl exceptionType) { 174 throw new Error("Operation handlesException not supported"); 175 } 176 177 eq CompilationUnit.getTypeDecl().handlesException(TypeDecl exceptionType) = false; 178 179 eq MethodDecl.getBlock().handlesException(TypeDecl exceptionType) = 180 throwsException(exceptionType); 181 182 syn lazy boolean MethodDecl.throwsException(TypeDecl exceptionType) { 183 for (Access exception : getExceptionList()) { 184 if (exceptionType.instanceOf(exception.type())) { 185 return true; 186 } 187 } 188 return false; 189 } 190 191 eq ConstructorDecl.getBlock().handlesException(TypeDecl exceptionType) = 192 throwsException(exceptionType); 193 194 eq ConstructorDecl.getParsedConstructorInvocation().handlesException(TypeDecl exceptionType) = 195 throwsException(exceptionType); 196 197 eq ConstructorDecl.getImplicitConstructorInvocation().handlesException(TypeDecl exceptionType) = 198 throwsException(exceptionType); 199 200 syn lazy boolean ConstructorDecl.throwsException(TypeDecl exceptionType) { 201 for (Access exception : getExceptionList()) { 202 if (exceptionType.instanceOf(exception.type())) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 eq FieldDeclaration.getInit().handlesException(TypeDecl exceptionType) { 210 if (hostType().isAnonymous()) { 211 return true; 212 } 213 for (Iterator iter = hostType().constructors().iterator(); iter.hasNext(); ) { 214 ConstructorDecl decl = (ConstructorDecl) iter.next(); 215 if (!decl.throwsException(exceptionType)) { 216 return false; 217 } 218 } 219 return true; 220 } 221 222 // 8.6 223 eq InstanceInitializer.getBlock().handlesException(TypeDecl exceptionType) { 224 if (hostType().isAnonymous()) { 225 return true; 226 } 227 for (Iterator iter = hostType().constructors().iterator(); iter.hasNext(); ) { 228 ConstructorDecl decl = (ConstructorDecl) iter.next(); 229 if (!decl.throwsException(exceptionType)) { 230 return false; 231 } 232 } 233 return true; 234 } 235 236 eq StaticInitializer.getBlock().handlesException(TypeDecl exceptionType) = 237 hostType().isAnonymous() && handlesException(exceptionType); 238 239 eq TryStmt.getCatchClause().handlesException(TypeDecl exceptionType) { 240 if (hasNonEmptyFinally() && !getFinally().canCompleteNormally()) { 241 return true; 242 } 243 return handlesException(exceptionType); 244 } 245 246 eq TryStmt.getBlock().handlesException(TypeDecl exceptionType) { 247 for (int i = 0; i < getNumCatchClause(); i++) { 248 if (getCatchClause(i).handles(exceptionType)) { 249 return true; 250 } 251 } 252 if (hasNonEmptyFinally() && !getFinally().canCompleteNormally()) { 253 return true; 254 } 255 return handlesException(exceptionType); 256 } 257 258 // the catch clause catches the exception 259 syn boolean CatchClause.handles(TypeDecl exceptionType) = false; 260 eq BasicCatch.handles(TypeDecl exceptionType) = 261 !getParameter().type().isUnknown() 262 && exceptionType.instanceOf(getParameter().type()); 263 264 /** 265 * The block of the try statement can throw an exception of 266 * a type assignable to the given type. 267 */ 268 syn lazy boolean TryStmt.catchableException(TypeDecl type) = 269 getBlock().reachedException(type); 270 271 protected boolean ASTNode.reachedException(TypeDecl type) { 272 for (int i = 0; i < getNumChild(); i++) { 273 if (getChild(i).reachedException(type)) { 274 return true; 275 } 276 } 277 return false; 278 } 279 280 protected boolean TryStmt.reachedException(TypeDecl type) { 281 boolean found = false; 282 // found is true if the exception type is caught by a catch clause 283 for (int i = 0; i < getNumCatchClause() && !found; i++) { 284 if (getCatchClause(i).handles(type)) { 285 found = true; 286 } 287 } 288 // if an exception is thrown in the block and the exception is not caught and 289 // either there is no finally block or the finally block can complete normally 290 if (!found && (!hasNonEmptyFinally() || getFinally().canCompleteNormally()) ) { 291 if (getBlock().reachedException(type)) { 292 return true; 293 } 294 } 295 // even if the exception is caught by the catch clauses they may 296 // throw new exceptions 297 for (int i = 0; i < getNumCatchClause(); i++) { 298 if (getCatchClause(i).reachedException(type)) { 299 return true; 300 } 301 } 302 return hasNonEmptyFinally() && getFinally().reachedException(type); 303 } 304 305 syn lazy boolean TypeDecl.mayCatch(TypeDecl thrownType) = 306 thrownType.instanceOf(this) || this.instanceOf(thrownType); 307 308 protected boolean MethodAccess.reachedException(TypeDecl catchType) { 309 for (Iterator iter = exceptionCollection().iterator(); iter.hasNext(); ) { 310 TypeDecl exceptionType = (TypeDecl) iter.next(); 311 if (catchType.mayCatch(exceptionType)) { 312 return true; 313 } 314 } 315 return super.reachedException(catchType); 316 } 317 318 protected boolean ThrowStmt.reachedException(TypeDecl catchType) { 319 TypeDecl exceptionType = getExpr().type(); 320 if (exceptionType == typeNull()) { 321 exceptionType = typeNullPointerException(); 322 } 323 if (catchType.mayCatch(exceptionType)) { 324 return true; 325 } 326 return super.reachedException(catchType); 327 } 328 329 // 8.8.4 (8.4.4) 330 protected boolean ConstructorAccess.reachedException(TypeDecl catchType) { 331 for (Access exception : decl().getExceptionList()) { 332 TypeDecl exceptionType = exception.type(); 333 if (catchType.mayCatch(exceptionType)) { 334 return true; 335 } 336 } 337 return super.reachedException(catchType); 338 } 339 340 protected boolean ClassInstanceExpr.reachedException(TypeDecl catchType) { 341 ConstructorDecl decl = decl(); 342 for (Access exception : decl().getExceptionList()) { 343 TypeDecl exceptionType = exception.type(); 344 if (catchType.mayCatch(exceptionType)) { 345 return true; 346 } 347 } 348 for (int i = 0; i < getNumArg(); i++) { 349 if (getArg(i).reachedException(catchType)) { 350 return true; 351 } 352 } 353 return false; 354 } 355 356 }