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