001 /* Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se> 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 PreciseRethrow { 032 033 syn Collection<TypeDecl> Expr.throwTypes() { 034 Collection<TypeDecl> tts = new LinkedList<TypeDecl>(); 035 tts.add(type()); 036 return tts; 037 } 038 039 eq VarAccess.throwTypes() = decl().throwTypes(); 040 041 syn lazy Collection<TypeDecl> VariableDeclaration.throwTypes() { 042 Collection<TypeDecl> tts = new LinkedList<TypeDecl>(); 043 tts.add(type()); 044 return tts; 045 } 046 047 syn lazy Collection<TypeDecl> FieldDeclaration.throwTypes() { 048 Collection<TypeDecl> tts = new LinkedList<TypeDecl>(); 049 tts.add(type()); 050 return tts; 051 } 052 053 syn lazy Collection<TypeDecl> ParameterDeclaration.throwTypes() { 054 if (isCatchParam() && isEffectivelyFinal()) { 055 // The catch parameter must be final to refine the throw type. 056 return catchClause().caughtExceptions(); 057 } else { 058 return Collections.singleton(type()); 059 } 060 } 061 062 syn lazy Collection<TypeDecl> CatchParameterDeclaration.throwTypes() = 063 catchClause().caughtExceptions(); 064 065 /** 066 * Note: this attribute deviates from what the JLS says about "effectively final", 067 * simply because the attribute name would be too confusing if it did not return true 068 * when the variable was explicitly declared final. The JLS considers declared final 069 * and effectively final to be mutually exclusive, we don't. 070 */ 071 syn boolean ParameterDeclaration.isEffectivelyFinal() = isFinal() || !inhModifiedInScope(this); 072 073 /** 074 * @return true if the variable var is modified in the local scope 075 */ 076 inh boolean ParameterDeclaration.inhModifiedInScope(Variable var); 077 eq ConstructorDecl.getParameter().inhModifiedInScope(Variable var) = 078 getBlock().modifiedInScope(var); 079 eq MethodDecl.getParameter().inhModifiedInScope(Variable var) = 080 hasBlock() && getBlock().modifiedInScope(var); 081 eq BasicCatch.getParameter().inhModifiedInScope(Variable var) = 082 getBlock().modifiedInScope(var); 083 084 syn boolean Stmt.modifiedInScope(Variable var); 085 eq VarDeclStmt.modifiedInScope(Variable var) { 086 for (VariableDeclaration decl: getSingleDeclList()) { 087 if (decl.modifiedInScope(var)) { 088 return true; 089 } 090 } 091 return false; 092 } 093 syn boolean VariableDeclaration.modifiedInScope(Variable var) = 094 hasInit() && getInit().modifiedInScope(var); 095 eq EmptyStmt.modifiedInScope(Variable var) = false; 096 eq LabeledStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var); 097 eq Block.modifiedInScope(Variable var) { 098 for (Stmt stmt : getStmtList()) { 099 if (stmt.modifiedInScope(var)) { 100 return true; 101 } 102 } 103 return false; 104 } 105 eq SwitchStmt.modifiedInScope(Variable var) = getBlock().modifiedInScope(var); 106 eq Case.modifiedInScope(Variable var) = false; 107 eq BreakStmt.modifiedInScope(Variable var) = false; 108 eq ContinueStmt.modifiedInScope(Variable var) = false; 109 eq ReturnStmt.modifiedInScope(Variable var) = false; 110 eq ThrowStmt.modifiedInScope(Variable var) = false; 111 eq IfStmt.modifiedInScope(Variable var) { 112 if (getThen().modifiedInScope(var)) { 113 return true; 114 } 115 return hasElse() && getElse().modifiedInScope(var); 116 } 117 eq WhileStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var); 118 eq DoStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var); 119 eq ForStmt.modifiedInScope(Variable var) { 120 for (Stmt stmt : getInitStmtList()) { 121 if (stmt.modifiedInScope(var)) { 122 return true; 123 } 124 } 125 for (Stmt stmt : getUpdateStmtList()) { 126 if (stmt.modifiedInScope(var)) { 127 return true; 128 } 129 } 130 return getStmt().modifiedInScope(var); 131 } 132 eq EnhancedForStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var); 133 eq SynchronizedStmt.modifiedInScope(Variable var) = getBlock().modifiedInScope(var); 134 eq TryStmt.modifiedInScope(Variable var) { 135 if (getBlock().modifiedInScope(var)) { 136 return true; 137 } 138 for (CatchClause cc : getCatchClauseList()) { 139 if (cc.modifiedInScope(var)) { 140 return true; 141 } 142 } 143 return hasNonEmptyFinally() && getFinally().modifiedInScope(var); 144 } 145 eq AssertStmt.modifiedInScope(Variable var) = false; 146 eq LocalClassDeclStmt.modifiedInScope(Variable var) = false; 147 148 syn boolean CatchClause.modifiedInScope(Variable var) = getBlock().modifiedInScope(var); 149 syn boolean Expr.modifiedInScope(Variable var) = false; 150 eq AbstractDot.modifiedInScope(Variable var) = getLeft().modifiedInScope(var); 151 eq VarAccess.modifiedInScope(Variable var) = false; 152 eq AssignExpr.modifiedInScope(Variable var) = 153 getDest().isVariable(var) || getSource().modifiedInScope(var); 154 eq ExprStmt.modifiedInScope(Variable var) = getExpr().modifiedInScope(var); 155 eq Binary.modifiedInScope(Variable var) = 156 getLeftOperand().modifiedInScope(var) || getRightOperand().modifiedInScope(var); 157 eq Unary.modifiedInScope(Variable var) = getOperand().modifiedInScope(var); 158 eq ParExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var); 159 eq CastExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var); 160 eq InstanceOfExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var); 161 eq ClassInstanceExpr.modifiedInScope(Variable var) { 162 for (int i = 0; i < getNumArg(); ++i) { 163 if (getArg(i).modifiedInScope(var)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 eq MethodAccess.modifiedInScope(Variable var) { 170 for (int i = 0; i < getNumArg(); ++i) { 171 if (getArg(i).modifiedInScope(var)) { 172 return true; 173 } 174 } 175 return false; 176 } 177 eq ConstructorAccess.modifiedInScope(Variable var) { 178 for (int i = 0; i < getNumArg(); ++i) { 179 if (getArg(i).modifiedInScope(var)) { 180 return true; 181 } 182 } 183 return false; 184 } 185 eq ArrayInit.modifiedInScope(Variable var) { 186 for (int i = 0; i < getNumInit(); ++i) { 187 if (getInit(i).modifiedInScope(var)) { 188 return true; 189 } 190 } 191 return false; 192 } 193 eq ArrayCreationExpr.modifiedInScope(Variable var) = 194 hasArrayInit() && getArrayInit().modifiedInScope(var); 195 eq ConditionalExpr.modifiedInScope(Variable var) = 196 getCondition().modifiedInScope(var) 197 || getTrueExpr().modifiedInScope(var) 198 || getFalseExpr().modifiedInScope(var); 199 200 syn boolean Expr.isVariable(Variable var) = false; 201 eq VarAccess.isVariable(Variable var) = decl() == var; 202 203 /** 204 * @return true if this is the parameter declaration of a catch clause 205 */ 206 inh boolean ParameterDeclaration.isCatchParam(); 207 eq Program.getChild(int i).isCatchParam() = false; 208 eq ConstructorDecl.getParameter().isCatchParam() = false; 209 eq MethodDecl.getParameter().isCatchParam() = false; 210 eq BasicCatch.getParameter().isCatchParam() = true; 211 212 inh CatchClause ParameterDeclaration.catchClause(); 213 inh CatchClause CatchParameterDeclaration.catchClause(); 214 215 eq Program.getChild(int i).catchClause() { 216 throw new IllegalStateException("Could not find parent " + "catch clause"); 217 } 218 219 eq CatchClause.getChild(int i).catchClause() = this; 220 221 inh Collection<TypeDecl> CatchClause.caughtExceptions(); 222 223 eq TryStmt.getCatchClause(int index).caughtExceptions() { 224 Collection<TypeDecl> excp = new HashSet<TypeDecl>(); 225 getBlock().collectExceptions(excp, this); 226 Collection<TypeDecl> caught = new LinkedList<TypeDecl>(); 227 Iterator<TypeDecl> iter = excp.iterator(); 228 while (iter.hasNext()) { 229 TypeDecl exception = iter.next(); 230 // this catch clause handles the exception 231 if (!getCatchClause(index).handles(exception)) { 232 continue; 233 } 234 // no previous catch clause handles the exception 235 boolean already = false; 236 for (int i = 0; i < index; ++i) { 237 if (getCatchClause(i).handles(exception)) { 238 already = true; 239 break; 240 } 241 } 242 if (!already) { 243 caught.add(exception); 244 } 245 } 246 return caught; 247 } 248 249 refine ExceptionHandling 250 public void ThrowStmt.exceptionHandling() { 251 Collection<TypeDecl> exceptionTypes = getExpr().throwTypes(); 252 for (TypeDecl exceptionType : exceptionTypes) { 253 if (exceptionType == typeNull()) { 254 exceptionType = typeNullPointerException(); 255 } 256 // 8.4.4 257 if (exceptionType.isCheckedException() && !handlesException(exceptionType)) { 258 errorf("%s throws uncaught exception %s", this.prettyPrint(), exceptionType.fullName()); 259 } 260 } 261 } 262 263 refine ExceptionHandling 264 protected boolean ThrowStmt.reachedException(TypeDecl catchType) { 265 Collection<TypeDecl> exceptionTypes = getExpr().throwTypes(); 266 boolean reached = false; 267 for (TypeDecl exceptionType : exceptionTypes) { 268 if (exceptionType == typeNull()) { 269 exceptionType = typeNullPointerException(); 270 } 271 if (catchType.mayCatch(exceptionType)) { 272 reached = true; 273 break; 274 } 275 if (super.reachedException(catchType)) { 276 reached = true; 277 break; 278 } 279 } 280 return reached; 281 } 282 283 inh boolean CatchClause.reportUnreachable(); 284 eq CatchClause.getBlock().reportUnreachable() = false; 285 286 void BasicCatch.checkUnreachableStmt() { 287 if (!getBlock().reachable() && reportUnreachable()) { 288 errorf("the exception %s is not thrown in the body of the try statement", 289 getParameter().type().fullName()); 290 } 291 } 292 293 void MultiCatch.checkUnreachableStmt() { 294 if (!getBlock().reachable() && reportUnreachable()) { 295 errorf("the exception %s is not thrown in the body of the try statement", 296 getParameter().type().fullName()); 297 } 298 } 299 }