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    }