001    /*
002     * JastAddJ is covered by the modified BSD License. You should have received
003     * a copy of the 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 PreciseRethrow {
010    
011        syn Collection<TypeDecl> Expr.throwTypes() {
012                Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
013                tts.add(type());
014                return tts;
015        }
016        eq VarAccess.throwTypes() = decl().throwTypes();
017        syn lazy Collection<TypeDecl> VariableDeclaration.throwTypes() {
018                Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
019                tts.add(type());
020                return tts;
021        }
022        syn lazy Collection<TypeDecl> FieldDeclaration.throwTypes() {
023                Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
024                tts.add(type());
025                return tts;
026        }
027        syn lazy Collection<TypeDecl> ParameterDeclaration.throwTypes() {
028                if (isCatchParam() && effectivelyFinal()) {
029                        // the catch parameter must be final or implicitly
030                        // final (multi-catch)
031                        return catchClause().caughtExceptions();
032                } else {
033                        Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
034                        tts.add(type());
035                        return tts;
036                }
037        }
038        syn lazy Collection<TypeDecl> CatchParameterDeclaration.throwTypes() =
039                catchClause().caughtExceptions();
040    
041        syn boolean ParameterDeclaration.effectivelyFinal() =
042                isFinal() || !inhModifiedInScope(this);
043    
044        /**
045         * @return true if the variable var is modified in the local scope
046         */
047        inh boolean ParameterDeclaration.inhModifiedInScope(Variable var);
048        eq ConstructorDecl.getParameter().inhModifiedInScope(Variable var) =
049                getBlock().modifiedInScope(var);
050        eq MethodDecl.getParameter().inhModifiedInScope(Variable var) =
051                getBlock().modifiedInScope(var);
052        eq BasicCatch.getParameter().inhModifiedInScope(Variable var) =
053                getBlock().modifiedInScope(var);
054    
055        syn boolean Stmt.modifiedInScope(Variable var);
056        eq VarDeclStmt.modifiedInScope(Variable var) = false;
057        eq VariableDeclaration.modifiedInScope(Variable var) = false;
058        eq EmptyStmt.modifiedInScope(Variable var) = false;
059        eq LabeledStmt.modifiedInScope(Variable var) =
060                getStmt().modifiedInScope(var);
061        eq ExprStmt.modifiedInScope(Variable var) =
062                getExpr().modifiedInScope(var);
063        eq Block.modifiedInScope(Variable var) {
064                for (Stmt stmt : getStmtList())
065                        if (stmt.modifiedInScope(var))
066                                return true;
067                return false;
068        }
069        eq SwitchStmt.modifiedInScope(Variable var) =
070                getBlock().modifiedInScope(var);
071        eq Case.modifiedInScope(Variable var) = false;
072        eq BreakStmt.modifiedInScope(Variable var) = false;
073        eq ContinueStmt.modifiedInScope(Variable var) = false;
074        eq ReturnStmt.modifiedInScope(Variable var) = false;
075        eq ThrowStmt.modifiedInScope(Variable var) = false;
076        eq IfStmt.modifiedInScope(Variable var) {
077                if (getThen().modifiedInScope(var))
078                        return true;
079                return hasElse() && getElse().modifiedInScope(var);
080        }
081        eq WhileStmt.modifiedInScope(Variable var) =
082                getStmt().modifiedInScope(var);
083        eq DoStmt.modifiedInScope(Variable var) =
084                getStmt().modifiedInScope(var);
085        eq ForStmt.modifiedInScope(Variable var) {
086                for (Stmt stmt : getInitStmtList())
087                        if (stmt.modifiedInScope(var))
088                                return true;
089                for (Stmt stmt : getUpdateStmtList())
090                        if (stmt.modifiedInScope(var))
091                                return true;
092                return getStmt().modifiedInScope(var);
093        }
094        eq EnhancedForStmt.modifiedInScope(Variable var) =
095                getStmt().modifiedInScope(var);
096        eq SynchronizedStmt.modifiedInScope(Variable var) =
097                getBlock().modifiedInScope(var);
098        eq TryStmt.modifiedInScope(Variable var) {
099                if (getBlock().modifiedInScope(var))
100                       return true;
101               for (CatchClause cc : getCatchClauseList())
102                       if (cc.modifiedInScope(var))
103                               return true;
104               return hasFinally() && getFinally().modifiedInScope(var);
105       }
106       eq AssertStmt.modifiedInScope(Variable var) = false;
107       eq LocalClassDeclStmt.modifiedInScope(Variable var) = false;
108    
109       syn boolean CatchClause.modifiedInScope(Variable var) =
110               getBlock().modifiedInScope(var);
111       syn boolean Expr.modifiedInScope(Variable var) = false;
112       eq AssignExpr.modifiedInScope(Variable var) =
113               getDest().isVariable(var);
114    
115       syn boolean Expr.isVariable(Variable var) = false;
116       eq VarAccess.isVariable(Variable var) = decl() == var;
117    
118       /**
119        * @return true if this is the parameter declaration of a catch clause
120        */
121       inh boolean ParameterDeclaration.isCatchParam();
122       eq Program.getChild(int i).isCatchParam() = false;
123       eq ConstructorDecl.getParameter().isCatchParam() = false;
124       eq MethodDecl.getParameter().isCatchParam() = false;
125       eq BasicCatch.getParameter().isCatchParam() = true;
126    
127       inh CatchClause ParameterDeclaration.catchClause();
128       inh CatchClause CatchParameterDeclaration.catchClause();
129    
130       eq Program.getChild(int i).catchClause() {
131               throw new IllegalStateException("Could not find parent " +
132                               "catch clause");
133       }
134       eq CatchClause.getChild(int i).catchClause() = this;
135    
136       inh Collection<TypeDecl> CatchClause.caughtExceptions();
137    
138       eq TryStmt.getCatchClause(int index).caughtExceptions() {
139               Collection<TypeDecl> excp = new HashSet<TypeDecl>();
140               getBlock().collectExceptions(excp, this);
141               Collection<TypeDecl> caught = new LinkedList<TypeDecl>();
142               Iterator<TypeDecl> iter = excp.iterator();
143               while (iter.hasNext()) {
144                       TypeDecl exception = iter.next();
145                       // this catch clause handles the exception
146                       if (!getCatchClause(index).handles(exception))
147                               continue;
148                       // no previous catch clause handles the exception
149                       boolean already = false;
150                       for (int i = 0; i < index; ++i) {
151                               if (getCatchClause(i).handles(exception)) {
152                                       already = true;
153                                       break;
154                               }
155                       }
156                       if (!already) {
157                               caught.add(exception);
158                       }
159               }
160               return caught;
161       }
162    
163       refine ExceptionHandling
164       public void ThrowStmt.exceptionHandling() {
165               Collection<TypeDecl> exceptionTypes = getExpr().throwTypes();
166               for (TypeDecl exceptionType : exceptionTypes) {
167                       if (exceptionType == typeNull())
168                               exceptionType = typeNullPointerException();
169                       // 8.4.4
170                       if (!handlesException(exceptionType))
171                               error(""+this+" throws uncaught exception "+
172                                               exceptionType.fullName());
173               }
174       }
175    
176       refine ExceptionHandling
177       protected boolean ThrowStmt.reachedException(TypeDecl catchType) {
178               Collection<TypeDecl> exceptionTypes = getExpr().throwTypes();
179               boolean reached = false;
180               for (TypeDecl exceptionType : exceptionTypes) {
181                       if(exceptionType == typeNull())
182                               exceptionType = typeNullPointerException();
183                       if(catchType.mayCatch(exceptionType)) {
184                               reached = true;
185                               break;
186                       }
187                       if (super.reachedException(catchType)) {
188                               reached = true;
189                               break;
190                       }
191               }
192               return reached;
193       }
194    
195       inh boolean CatchClause.reportUnreachable();
196       eq CatchClause.getBlock().reportUnreachable() = false;
197    
198       void BasicCatch.checkUnreachableStmt() {
199               if (!getBlock().reachable() && reportUnreachable())
200                       error("the exception "+getParameter().type().fullName()+
201                       " is not thrown in the body of the try statement");
202       }
203    
204       void MultiCatch.checkUnreachableStmt() {
205               if (!getBlock().reachable() && reportUnreachable())
206                       error("the exception "+getParameter().type().fullName()+
207                       " is not thrown in the body of the try statement");
208       }
209    }