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 TryWithResources { 032 /** 033 * Syntactic classification for TWR resource declarations. 034 */ 035 eq ResourceDeclaration.getTypeAccess().nameType() = NameType.TYPE_NAME; 036 037 /** 038 * Inherit the lookupType attribute in ResourceDeclaration. 039 */ 040 inh TypeDecl ResourceDeclaration.lookupType(String packageName, String typeName); 041 042 /** 043 * Type checking for TWR. 044 */ 045 public void ResourceDeclaration.typeCheck() { 046 TypeDecl typeAutoCloseable = lookupType("java.lang", "AutoCloseable"); 047 if (typeAutoCloseable.isUnknown()) { 048 error("java.lang.AutoCloseable not found"); 049 } else if (!getTypeAccess().type().instanceOf(typeAutoCloseable)) { 050 error("Resource specification must declare an AutoCloseable resource"); 051 } 052 } 053 054 /*coll Set<TypeDecl> Stmt.uncaughtExceptions() 055 [new HashSet<TypeDecl>()] with add; 056 057 MethodAccess contributes exceptionCollection() 058 to Stmt.uncaughtExceptions();*/ 059 060 /** 061 * Exception error checks. 062 */ 063 public void TryWithResources.exceptionHandling() { 064 065 // Check exception handling of exceptions on auto closing of resource 066 for (ResourceDeclaration resource : getResourceList()) { 067 MethodDecl close = lookupClose(resource); 068 if (close == null) { 069 continue; 070 } 071 for (Access exception : close.getExceptionList()) { 072 TypeDecl exceptionType = exception.type(); 073 if (!twrHandlesException(exceptionType)) { 074 error("automatic closing of resource " + resource.name() 075 + " may raise the uncaught exception " + exceptionType.fullName() + "; " 076 + "it must be caught or declared as being thrown"); 077 } 078 } 079 } 080 } 081 082 /** 083 * This attribute computes whether or not the TWR statement 084 * has a catch clause which handles the exception. 085 */ 086 syn boolean TryWithResources.catchHandlesException(TypeDecl exceptionType) { 087 for (int i = 0; i < getNumCatchClause(); i++) { 088 if (getCatchClause(i).handles(exceptionType)) { 089 return true; 090 } 091 } 092 return false; 093 } 094 095 /** 096 * Returns true if exceptions of type exceptionType are handled 097 * in the try-with-resources statement or any containing statement 098 * within the directly enclosing method or initializer block. 099 */ 100 syn boolean TryWithResources.twrHandlesException(TypeDecl exceptionType) { 101 if (catchHandlesException(exceptionType)) { 102 return true; 103 } 104 if (hasNonEmptyFinally() && !getFinally().canCompleteNormally()) { 105 return true; 106 } 107 return handlesException(exceptionType); 108 } 109 110 /** 111 * Inherit the handlesException attribute from methoddecl. 112 */ 113 inh lazy boolean TryWithResources.handlesException(TypeDecl exceptionType); 114 115 eq TryWithResources.getResource(int i).handlesException(TypeDecl exceptionType) = 116 twrHandlesException(exceptionType); 117 118 eq TryWithResources.getBlock().handlesException(TypeDecl exceptionType) = 119 twrHandlesException(exceptionType); 120 121 122 /** 123 * Lookup the close method declaration for the resource which is being used. 124 */ 125 syn MethodDecl TryWithResources.lookupClose(ResourceDeclaration resource) { 126 TypeDecl resourceType = resource.getTypeAccess().type(); 127 for (MethodDecl method : (Collection<MethodDecl>) resourceType.memberMethods("close")) { 128 if (method.getNumParameter() == 0) { 129 return method; 130 } 131 } 132 return null; 133 /* We can't throw a runtime exception here. If there is no close method it 134 * likely means that the resource type is not a subtype of java.lang.AutoCloseable 135 * and type checking will report this error. 136 */ 137 //throw new RuntimeException("close() not found for resource type "+resourceType.fullName()); 138 } 139 140 inh lazy TypeDecl TryWithResources.typeError(); 141 inh lazy TypeDecl TryWithResources.typeRuntimeException(); 142 143 eq TryWithResources.getCatchClause(int childIndex).reachableCatchClause(TypeDecl exceptionType) { 144 for (int i = 0; i < childIndex; i++) { 145 if (getCatchClause(i).handles(exceptionType)) { 146 return false; 147 } 148 } 149 if (catchableException(exceptionType)) { 150 return true; 151 } 152 if (exceptionType.mayCatch(typeError()) || exceptionType.mayCatch(typeRuntimeException())) { 153 return true; 154 } 155 return false; 156 } 157 158 /** 159 * Variable lookup attribute. 160 */ 161 eq TryWithResources.getBlock().lookupVariable(String name) = localLookup(name); 162 syn lazy SimpleSet TryWithResources.localLookup(String name) { 163 VariableDeclaration v = localVariableDeclaration(name); 164 if (v != null) { 165 return v; 166 } 167 return lookupVariable(name); 168 } 169 syn lazy VariableDeclaration TryWithResources.localVariableDeclaration(String name) { 170 for (ResourceDeclaration resource : getResourceList()) { 171 if (resource.declaresVariable(name)) { 172 return resource; 173 } 174 } 175 return null; 176 } 177 178 TryWithResources implements VariableScope; 179 inh lazy SimpleSet TryWithResources.lookupVariable(String name); 180 eq TryWithResources.getResource(int i).outerScope() = this; 181 182 inh boolean VariableDeclaration.resourcePreviouslyDeclared(String name); 183 inh boolean TryWithResources.resourcePreviouslyDeclared(String name); 184 eq TryWithResources.getResource(int index).resourcePreviouslyDeclared(String name) { 185 for (int i = 0; i < index; ++i) { 186 if (getResource(i).name().equals(name)) { 187 return true; 188 } 189 } 190 return false; 191 } 192 eq BodyDecl.getChild(int i).resourcePreviouslyDeclared(String name) = false; 193 eq Program.getChild(int i).resourcePreviouslyDeclared(String name) = false; 194 195 public void ResourceDeclaration.nameCheck() { 196 // Special name check for resource specification 197 if (resourcePreviouslyDeclared(name())) { 198 errorf("A resource with the name %s has already been declared in this try statement.", 199 name()); 200 } 201 202 // Do regular name check for variable declaration 203 super.nameCheck(); 204 } 205 206 // Definite Assignment 207 eq TryWithResources.isDAafter(Variable v) = getBlock().isDAafter(v); 208 209 // 16.2.2 7th bullet 210 eq TryWithResources.getResource(int index).isDAbefore(Variable v) = 211 index == 0 ? isDAbefore(v) : getResource(index - 1).isDAafter(v); 212 213 eq TryWithResources.getBlock().isDAbefore(Variable v) = 214 getNumResource() == 0 215 ? isDAbefore(v) 216 : getResource(getNumResource() - 1).isDAafter(v); 217 218 /** 219 * Returns true if the try-with-resources statement can throw 220 * an exception of type (or a subtype of) catchType. 221 */ 222 protected boolean TryWithResources.reachedException(TypeDecl catchType) { 223 boolean found = false; 224 // found is true if the exception type is caught by a catch clause 225 for (int i = 0; i < getNumCatchClause() && !found; i++) { 226 if (getCatchClause(i).handles(catchType)) { 227 found = true; 228 } 229 } 230 // If an exception is thrown in the block and the exception is not caught and 231 // either there is no finally block or the finally block can complete normally. 232 if (!found && (!hasNonEmptyFinally() || getFinally().canCompleteNormally()) ) { 233 if (catchableException(catchType)) { 234 return true; 235 } 236 } 237 // Even if the exception is caught by the catch clauses they may 238 // throw new exceptions. 239 for (int i = 0; i < getNumCatchClause(); i++) { 240 if (getCatchClause(i).reachedException(catchType)) { 241 return true; 242 } 243 } 244 return hasNonEmptyFinally() && getFinally().reachedException(catchType); 245 } 246 247 /** 248 * True if the automatic closing of resources in this try-with-resources statement 249 * may throw an exception of type catchType. 250 */ 251 syn boolean TryWithResources.resourceClosingException(TypeDecl catchType) { 252 for (ResourceDeclaration resource : getResourceList()) { 253 MethodDecl close = lookupClose(resource); 254 if (close == null) { 255 continue; 256 } 257 for (Access exception : close.getExceptionList()) { 258 TypeDecl exceptionType = exception.type(); 259 if (catchType.mayCatch(exception.type())) { 260 return true; 261 } 262 } 263 } 264 return false; 265 } 266 267 /** 268 * True if the resource initialization of this try-with-resources statement 269 * may throw an exception of type catchType. 270 */ 271 syn boolean TryWithResources.resourceInitializationException(TypeDecl catchType) { 272 for (ResourceDeclaration resource : getResourceList()) { 273 if (resource.reachedException(catchType)) { 274 return true; 275 } 276 } 277 return false; 278 } 279 280 /** 281 * @see AST.TryStmt#catchableException(TypeDecl) TryStmt.catchableException(TypeDecl) 282 */ 283 eq TryWithResources.catchableException(TypeDecl type) = 284 getBlock().reachedException(type) 285 || resourceClosingException(type) 286 || resourceInitializationException(type); 287 288 eq ResourceModifiers.isFinal() = true; 289 } 290