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