001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    aspect ExceptionHandling {
011    
012      // exception types
013      eq Program.getChild().typeException() = lookupType("java.lang", "Exception");
014      inh lazy TypeDecl TypeDecl.typeException();
015      eq Program.getChild().typeRuntimeException() = lookupType("java.lang", "RuntimeException");
016      inh lazy TypeDecl TypeDecl.typeRuntimeException();
017      eq Program.getChild().typeError() = lookupType("java.lang", "Error");
018      inh lazy TypeDecl TypeDecl.typeError();
019      eq Program.getChild().typeNullPointerException() = lookupType("java.lang", "NullPointerException");
020      inh lazy TypeDecl ThrowStmt.typeNullPointerException();
021      eq Program.getChild().typeThrowable() = lookupType("java.lang", "Throwable");
022      inh lazy TypeDecl BodyDecl.typeThrowable();
023    
024      syn lazy boolean TypeDecl.isException() = instanceOf(typeException());
025    
026      /**
027       * Unfortunately the concept of checked vs. unchecked exceptions
028       * has been inverted in JastAddJ compared to the Java specification.
029       * This is a slightly unfortunate design flaw which we cannot change
030       * at this time.
031       */
032      syn lazy boolean TypeDecl.isCheckedException() = isException() &&
033        (instanceOf(typeRuntimeException()) || instanceOf(typeError()));
034    
035      /**
036       * Unfortunately the concept of checked vs. unchecked exceptions
037       * has been inverted in JastAddJ compared to the Java specification.
038       * This is a slightly unfortunate design flaw which we cannot change
039       * at this time.
040       */
041      syn lazy boolean TypeDecl.isUncheckedException() = isException() && !isCheckedException();
042    
043      inh boolean MethodAccess.handlesException(TypeDecl exceptionType);
044      inh boolean ConstructorAccess.handlesException(TypeDecl exceptionType);
045      inh lazy boolean ThrowStmt.handlesException(TypeDecl exceptionType);
046      inh lazy boolean InstanceInitializer.handlesException(TypeDecl exceptionType);
047      inh lazy boolean StaticInitializer.handlesException(TypeDecl exceptionType);
048      inh boolean FieldDeclaration.handlesException(TypeDecl exceptionType);
049      inh lazy boolean TryStmt.handlesException(TypeDecl exceptionType);
050      inh lazy boolean ConstructorDecl.handlesException(TypeDecl exceptionType);
051      inh lazy boolean MethodDecl.handlesException(TypeDecl exceptionType);
052      inh boolean ClassInstanceExpr.handlesException(TypeDecl exceptionType);
053      
054      public void ASTNode.exceptionHandling() {
055      }
056      
057      public void MethodAccess.exceptionHandling() {
058        for(Iterator iter = exceptionCollection().iterator(); iter.hasNext(); ) {
059          TypeDecl exceptionType = (TypeDecl)iter.next();
060          if(!handlesException(exceptionType))
061            error("" + decl().hostType().fullName() + "." + this + " invoked in " + hostType().fullName() + " may throw uncaught exception " + exceptionType.fullName());
062        }
063      }
064    
065      syn lazy Collection MethodAccess.exceptionCollection() {
066        //System.out.println("Computing exceptionCollection for " + name());
067        HashSet set = new HashSet();
068        Iterator iter = decls().iterator();
069        if(!iter.hasNext())
070          return set;
071    
072        MethodDecl m = (MethodDecl)iter.next();
073        //System.out.println("Processing first found method " + m.signature() + " in " + m.hostType().fullName());
074    
075        for(int i = 0; i < m.getNumException(); i++) {
076          TypeDecl exceptionType = m.getException(i).type();
077          set.add(exceptionType);
078        }
079        while(iter.hasNext()) {
080          HashSet first = new HashSet();
081          first.addAll(set);
082          HashSet second = new HashSet();
083          m = (MethodDecl)iter.next();
084          //System.out.println("Processing the next method " + m.signature() + " in " + m.hostType().fullName());
085          for(int i = 0; i < m.getNumException(); i++) {
086            TypeDecl exceptionType = m.getException(i).type();
087            second.add(exceptionType);
088          }
089          set = new HashSet();
090          for(Iterator i1 = first.iterator(); i1.hasNext(); ) {
091            TypeDecl firstType = (TypeDecl)i1.next(); 
092            for(Iterator i2 = second.iterator(); i2.hasNext(); ) {
093              TypeDecl secondType = (TypeDecl)i2.next();
094              if(firstType.instanceOf(secondType)) {
095                set.add(firstType);
096              }
097              else if(secondType.instanceOf(firstType)) {
098                set.add(secondType);
099              }
100            }
101          }
102        }
103        return set;
104      }
105    
106      public void ClassDecl.exceptionHandling() {
107        constructors();
108        super.exceptionHandling();
109      }
110    
111      public void ConstructorAccess.exceptionHandling() {
112        for(int i = 0; i < decl().getNumException(); i++) {
113          TypeDecl exceptionType = decl().getException(i).type();
114          if(!handlesException(exceptionType))
115            error("" + this + " may throw uncaught exception " + exceptionType.fullName());
116        }
117      }
118    
119      public void ClassInstanceExpr.exceptionHandling() {
120        for (Access exception : decl().getExceptionList()) {
121          TypeDecl exceptionType = exception.type();
122          if (!handlesException(exceptionType))
123            error("" + this + " may throw uncaught exception " +
124                exceptionType.fullName() + "; it must be caught or declared as being thrown");
125        }
126      }
127    
128      public void ThrowStmt.exceptionHandling() {
129        TypeDecl exceptionType = getExpr().type();
130        if(exceptionType == typeNull())
131          exceptionType = typeNullPointerException();
132        // 8.4.4
133        if(!handlesException(exceptionType))
134          error("" + this + " throws uncaught exception " + exceptionType.fullName());
135      }
136    
137      eq Program.getChild().handlesException(TypeDecl exceptionType) {
138        throw new Error("Operation handlesException not supported");
139      }
140      eq CompilationUnit.getTypeDecl().handlesException(TypeDecl exceptionType) =
141        !exceptionType.isUncheckedException();
142      
143      eq MethodDecl.getBlock().handlesException(TypeDecl exceptionType) =
144        throwsException(exceptionType) || handlesException(exceptionType);
145      
146      syn lazy boolean MethodDecl.throwsException(TypeDecl exceptionType) {
147        for(int i = 0; i < getNumException(); i++)
148          if(exceptionType.instanceOf(getException(i).type()))
149            return true;
150        return false;
151      }
152      
153      eq ConstructorDecl.getBlock().handlesException(TypeDecl exceptionType) = 
154        throwsException(exceptionType) || handlesException(exceptionType);
155    
156      eq ConstructorDecl.getConstructorInvocation().handlesException(TypeDecl exceptionType) = 
157        throwsException(exceptionType) || handlesException(exceptionType);
158    
159      syn lazy boolean ConstructorDecl.throwsException(TypeDecl exceptionType) {
160        for(int i = 0; i < getNumException(); i++)
161          if(exceptionType.instanceOf(getException(i).type()))
162            return true;
163        return false;
164      }
165    
166      eq FieldDeclaration.getInit().handlesException(TypeDecl exceptionType) {
167        if(hostType().isAnonymous())
168          return true;
169        if(!exceptionType.isUncheckedException())
170          return true;
171        for(Iterator iter = hostType().constructors().iterator(); iter.hasNext(); ) {
172          ConstructorDecl decl = (ConstructorDecl)iter.next();
173          if(!decl.throwsException(exceptionType))
174            return false;
175        }
176        return true;
177      }
178    
179      // 8.6
180      eq InstanceInitializer.getBlock().handlesException(TypeDecl exceptionType) {
181        if(hostType().isAnonymous())
182          return true;
183        if(!exceptionType.isUncheckedException())
184          return true;
185        for(Iterator iter = hostType().constructors().iterator(); iter.hasNext(); ) {
186          ConstructorDecl decl = (ConstructorDecl)iter.next();
187          if(!decl.throwsException(exceptionType))
188            return false;
189        }
190        return true;
191      }
192      
193      eq StaticInitializer.getBlock().handlesException(TypeDecl exceptionType) =
194        hostType().isAnonymous() ? handlesException(exceptionType) : !exceptionType.isUncheckedException();
195      
196      eq TryStmt.getCatchClause().handlesException(TypeDecl exceptionType) {
197        if(hasFinally() && !getFinally().canCompleteNormally())
198          return true;
199        return handlesException(exceptionType);
200      }
201      
202      eq TryStmt.getBlock().handlesException(TypeDecl exceptionType) {
203        for(int i = 0; i < getNumCatchClause(); i++)
204          if(getCatchClause(i).handles(exceptionType))
205            return true;
206        if(hasFinally() && !getFinally().canCompleteNormally())
207          return true;
208        return handlesException(exceptionType);
209      }
210    
211      // the catch clause catches the exception
212      syn boolean CatchClause.handles(TypeDecl exceptionType) = false;
213      eq BasicCatch.handles(TypeDecl exceptionType) =
214        !getParameter().type().isUnknown()
215        && exceptionType.instanceOf(getParameter().type());
216        
217      /**
218       * The block of the try statement can throw an exception of
219       * a type assignable to the given type.
220       */
221      syn lazy boolean TryStmt.catchableException(TypeDecl type) =
222        getBlock().reachedException(type);
223    
224      protected boolean ASTNode.reachedException(TypeDecl type) {
225        for(int i = 0; i < getNumChild(); i++)
226          if(getChild(i).reachedException(type))
227            return true;
228        return false;
229      }
230    
231      protected boolean TryStmt.reachedException(TypeDecl type) {
232        boolean found = false;
233        // found is true if the exception type is caught by a catch clause
234        for(int i = 0; i < getNumCatchClause() && !found; i++)
235          if(getCatchClause(i).handles(type))
236            found = true;
237        // if an exception is thrown in the block and the exception is not caught and
238        // either there is no finally block or the finally block can complete normally
239        if(!found && (!hasFinally() || getFinally().canCompleteNormally()) )
240          if(getBlock().reachedException(type))
241            return true;
242        // even if the exception is caught by the catch clauses they may 
243        // throw new exceptions
244        for(int i = 0; i < getNumCatchClause(); i++)
245          if(getCatchClause(i).reachedException(type))
246            return true;
247        return hasFinally() && getFinally().reachedException(type);
248      }
249    
250      syn lazy boolean TypeDecl.mayCatch(TypeDecl thrownType) =
251        thrownType.instanceOf(this) || this.instanceOf(thrownType);
252    
253      protected boolean MethodAccess.reachedException(TypeDecl catchType) {
254        for(Iterator iter = exceptionCollection().iterator(); iter.hasNext(); ) {
255          TypeDecl exceptionType = (TypeDecl)iter.next();
256          if(catchType.mayCatch(exceptionType))
257            return true;
258        }
259        return super.reachedException(catchType);
260      }
261      
262      protected boolean ThrowStmt.reachedException(TypeDecl catchType) {
263        TypeDecl exceptionType = getExpr().type();
264        if(exceptionType == typeNull())
265          exceptionType = typeNullPointerException();
266        if(catchType.mayCatch(exceptionType))
267          return true;
268        return super.reachedException(catchType);
269      }
270      
271      // 8.8.4 (8.4.4)
272      protected boolean ConstructorAccess.reachedException(TypeDecl catchType) {
273        for(int i = 0; i < decl().getNumException(); i++) {
274          TypeDecl exceptionType = decl().getException(i).type();
275          if(catchType.mayCatch(exceptionType))
276            return true;
277        }
278        return super.reachedException(catchType);
279      }
280    
281      protected boolean ClassInstanceExpr.reachedException(TypeDecl catchType) {
282        ConstructorDecl decl = decl();
283        for(int i = 0; i < decl.getNumException(); i++) {
284          TypeDecl exceptionType = decl.getException(i).type();
285          if(catchType.mayCatch(exceptionType))
286            return true;
287        }
288        for(int i = 0; i < getNumArg(); i++) {
289          if(getArg(i).reachedException(catchType))
290            return true;
291        }
292        return false;
293      }
294      
295    }