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 /** 034 * An NTA which is used for code generation. 035 * 036 * This NTA will handle the recursive nature of code 037 * generation for try-with-resources statements. 038 */ 039 syn nta Stmt TryWithResources.getTransformed() { 040 if (getNumCatchClause() == 0 && !hasNonEmptyFinally()) { 041 // transform to BasicTWR 042 Block block; 043 if (getNumResource() == 1) { 044 block = (Block) getBlock().treeCopyNoTransform(); 045 } else { 046 block = new Block(); 047 List<ResourceDeclaration> resourceListTail = new List<ResourceDeclaration>(); 048 for (int i = 1; i < getNumResource(); ++i) { 049 resourceListTail.add((ResourceDeclaration) 050 getResource(i).treeCopyNoTransform()); 051 } 052 block.addStmt(new TryWithResources( 053 resourceListTail, 054 (Block) getBlock().treeCopyNoTransform(), 055 new List<CatchClause>(), 056 new Opt<Block>())); 057 } 058 return new BasicTWR( 059 (ResourceDeclaration) 060 getResource(0).treeCopyNoTransform(), 061 block); 062 } else { 063 // transform to surrounding try stmt 064 Block block = new Block(); 065 block.addStmt(new TryWithResources( 066 (List<ResourceDeclaration>) 067 getResourceList().treeCopyNoTransform(), 068 (Block) getBlock().treeCopyNoTransform(), 069 new List<CatchClause>(), 070 new Opt<Block>())); 071 072 return new TryStmt(block, 073 (List<CatchClause>) getCatchClauseList().treeCopyNoTransform(), 074 (Opt<Block>) getFinallyOpt().treeCopyNoTransform()); 075 } 076 } 077 078 /** 079 * Code generation for the try-with-resources statement. 080 */ 081 public void TryWithResources.createBCode(CodeGeneration gen) { 082 083 getTransformed().createBCode(gen); 084 } 085 086 /** 087 * The general structure of the basic try-with-resources: 088 * 089 * <pre><code> 090 * RESOURCE 091 * BLOCK 092 * 093 * Primary Exception Handler 094 * Automatic Closing of Resource 095 * Suppressed Exception Handler 096 * re-throw primary exception 097 * Automatic Closing of Resource 098 * </pre></code> 099 * 100 * Pseudocode for basic try-with-resources: 101 * 102 * <pre><code> 103 * 0 .resourceBegin 104 * 1 emit RESOURCE 105 * 0 store resource 106 * 0 .resourceEnd 107 * 108 * 0 .blockBegin 109 * 0 emit BLOCK 110 * 0 .blockEnd 111 * 0 goto outerFinally 112 * 113 * 1 .resourceException 114 * 1 throw 115 * 116 * #if BLOCK is not empty: 117 * 118 * 1 .catchPrimary 119 * 0 store primary 120 * 121 * 0 .tryCloseBegin 122 * 1 load resource 123 * 0 ifnull innerFinally 124 * 1 load resource 125 * 0 invoke java.lang.AutoCloseable.close() 126 * 0 .tryCloseEnd 127 * 128 * 0 goto innerFinally 129 * 130 * 1 .catchSuppressed 131 * 0 store suppressed 132 * 1 load primary 133 * 2 load suppressed 134 * 0 invoke java.lang.Throwable.addSuppressed(Throwable) 135 * 136 * 0 .innerFinally 137 * 1 load primary 138 * 1 throw 139 * 140 * #endif BLOCK is not empty 141 * 142 * 0 .outerFinally 143 * 1 load resource 144 * 0 ifnull tryEnd 145 * 1 load resource 146 * 0 invoke java.lang.AutoCloseable.close() 147 * 148 * 0 .tryEnd 149 * 150 * Exception Table: 151 * resourceBegin .. resourceEnd : resourceException 152 * blockBegin .. blockEnd : catchPrimary 153 * tryCloseBegin .. tryCloseEnd : catchSuppressed 154 * </pre></code> 155 * 156 */ 157 public void BasicTWR.createBCode(CodeGeneration gen) { 158 ResourceDeclaration resource = getResource(); 159 160 int resourceBeginLbl = gen.constantPool().newLabel(); 161 int resourceEndLbl = gen.constantPool().newLabel(); 162 int blockBeginLbl = gen.constantPool().newLabel(); 163 int blockEndLbl = gen.constantPool().newLabel(); 164 int tryCloseBeginLbl = gen.constantPool().newLabel(); 165 int tryCloseEndLbl = gen.constantPool().newLabel(); 166 167 int resourceExceptionLbl = gen.constantPool().newLabel(); 168 int catchPrimaryLbl = gen.constantPool().newLabel(); 169 int catchSuppressedLbl = gen.constantPool().newLabel(); 170 int innerFinallyLbl = gen.constantPool().newLabel(); 171 int outerFinallyLbl = gen.constantPool().newLabel(); 172 int tryEndLbl = gen.constantPool().newLabel(); 173 174 TypeDecl throwableType = lookupType("java.lang", "Throwable"); 175 TypeDecl resourceType = resource.type(); 176 TypeDecl autoCloseableType = lookupType("java.lang", "AutoCloseable"); 177 178 gen.changeStackDepth(3); 179 int resourceIndex = resource.localNum(); 180 int primaryIndex = resourceIndex+resourceType.variableSize(); 181 int suppressedIndex = primaryIndex+throwableType.variableSize(); 182 183 // store the resource in local 184 gen.addLabel(resourceBeginLbl); 185 resource.createBCode(gen); 186 gen.addLabel(resourceEndLbl); 187 gen.emit(Bytecode.NOP); 188 189 gen.addLabel(blockBeginLbl); 190 getBlock().createBCode(gen); 191 gen.addLabel(blockEndLbl); 192 gen.emitGoto(outerFinallyLbl); 193 194 // If there was an exception when initializing the resource 195 // we need to directly rethrow the exception 196 gen.addLabel(resourceExceptionLbl); 197 gen.emitThrow(); 198 gen.addException( 199 resourceBeginLbl, 200 resourceEndLbl, 201 resourceExceptionLbl, 202 CodeGeneration.ExceptionEntry.CATCH_ALL); 203 204 if (gen.addressOf(blockBeginLbl) != gen.addressOf(blockEndLbl)) { 205 206 // catch primary exception 207 // operand stack: .., #primary 208 gen.addLabel(catchPrimaryLbl); 209 throwableType.emitStoreLocal(gen, primaryIndex); 210 211 // try-close resource 212 gen.addLabel(tryCloseBeginLbl); 213 { 214 // if resource != null 215 resourceType.emitLoadLocal(gen, resourceIndex); 216 gen.emitCompare(Bytecode.IFNULL, innerFinallyLbl); 217 resourceType.emitLoadLocal(gen, resourceIndex); 218 closeMethod().emitInvokeMethod(gen, autoCloseableType); 219 } 220 gen.addLabel(tryCloseEndLbl); 221 gen.emitGoto(innerFinallyLbl); 222 223 // catch suppressed exception 224 // operand stack: .., #primary, #suppressed 225 gen.addLabel(catchSuppressedLbl); 226 throwableType.emitStoreLocal(gen, suppressedIndex); 227 throwableType.emitLoadLocal(gen, primaryIndex); 228 throwableType.emitLoadLocal(gen, suppressedIndex); 229 addSuppressedMethod().emitInvokeMethod(gen, throwableType); 230 231 // inner finally 232 // operand stack: .., #primary 233 gen.addLabel(innerFinallyLbl); 234 throwableType.emitLoadLocal(gen, primaryIndex); 235 gen.emitThrow(); 236 237 // If there was an exception during the block of the try 238 // statement, then we should try to close the resource 239 gen.addException( 240 blockBeginLbl, 241 blockEndLbl, 242 catchPrimaryLbl, 243 CodeGeneration.ExceptionEntry.CATCH_ALL); 244 245 // If an exception occurrs during the automatic closing 246 // of a resource after an exception in the try block... 247 gen.addException( 248 tryCloseBeginLbl, 249 tryCloseEndLbl, 250 catchSuppressedLbl, 251 CodeGeneration.ExceptionEntry.CATCH_ALL); 252 } 253 254 // outer finally 255 gen.addLabel(outerFinallyLbl); 256 { 257 // if resource != null 258 resourceType.emitLoadLocal(gen, resourceIndex); 259 gen.emitCompare(Bytecode.IFNULL, tryEndLbl); 260 resourceType.emitLoadLocal(gen, resourceIndex); 261 closeMethod().emitInvokeMethod(gen, autoCloseableType); 262 } 263 264 gen.addLabel(tryEndLbl); 265 gen.emit(Bytecode.NOP); 266 } 267 268 /** 269 * Lookup the java.lang.Throwable.close() method. 270 */ 271 private MethodDecl BasicTWR.closeMethod() { 272 TypeDecl autoCloseableType = lookupType("java.lang", "AutoCloseable"); 273 if (autoCloseableType.isUnknown()) { 274 throw new Error("Could not find java.lang.AutoCloseable"); 275 } 276 for (MethodDecl method : (Collection<MethodDecl>) 277 autoCloseableType.memberMethods("close")) { 278 if (method.getNumParameter() == 0) { 279 return method; 280 } 281 } 282 throw new Error("Could not find java.lang.AutoCloseable.close()"); 283 } 284 285 /** 286 * Lookup the java.lang.Throwable.addSuppressed(Throwable) method. 287 */ 288 private MethodDecl BasicTWR.addSuppressedMethod() { 289 TypeDecl throwableType = lookupType("java.lang", "Throwable"); 290 if (throwableType.isUnknown()) { 291 throw new Error("Could not find java.lang.Throwable"); 292 } 293 for (MethodDecl method : (Collection<MethodDecl>) 294 throwableType.memberMethods("addSuppressed")) { 295 if (method.getNumParameter() == 1 && 296 method.getParameter(0).getTypeAccess().type() == throwableType) { 297 return method; 298 } 299 } 300 throw new Error("Could not find java.lang.Throwable.addSuppressed()"); 301 } 302 303 /** 304 * Local indices to store the resources. 305 */ 306 eq BasicTWR.getResource().localNum() = localNum(); 307 308 eq TryWithResources.getResource(int index).localNum() = 50; 309 eq TryWithResources.getBlock().localNum() = 100; 310 311 /** 312 * Local indices for the block are adjusted to account for the resource 313 * variables. 314 */ 315 eq BasicTWR.getBlock().localNum() = 316 getResource().localNum() 317 + getResource().type().variableSize() 318 + 2 * lookupType("java.lang", "Throwable").variableSize(); 319 }