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    import java.util.HashSet;
011    
012    aspect BranchTarget {
013      
014      // public interface
015      
016      // find the target statement for break and continue
017      // this can be a try statement with a finally block that can not complete normally
018      syn lazy Stmt BreakStmt.targetStmt();
019      syn lazy Stmt ContinueStmt.targetStmt();
020    
021      // an ordered list of TryStmts with finally blocks that are reached until the final target of this statement is reached
022      syn lazy ArrayList BreakStmt.finallyList();
023      syn lazy ArrayList ContinueStmt.finallyList();
024      syn lazy ArrayList ReturnStmt.finallyList();
025      
026    
027      // ---------------------------------------------------------------------------
028    
029      // propagate branch statements to the statements that are their respective
030      // targets taking finally blocks that can not complete normally into account
031      interface BranchPropagation {
032        syn lazy Collection targetContinues();
033        syn lazy Collection targetBreaks();
034        syn lazy Collection targetBranches();
035        syn lazy Collection escapedBranches();
036        syn lazy Collection branches();
037        //public void collectBranches(Collection c);
038        
039        syn lazy boolean targetOf(ContinueStmt stmt);
040        syn lazy boolean targetOf(BreakStmt stmt);
041      }
042      BranchTargetStmt implements BranchPropagation;
043      
044      public void ASTNode.collectBranches(Collection c) {
045        for(int i = 0; i < getNumChild(); i++)
046          getChild(i).collectBranches(c);
047      }
048      public void ContinueStmt.collectBranches(Collection c) {
049        c.add(this);
050      }
051      public void BreakStmt.collectBranches(Collection c) {
052        c.add(this);
053      }
054      public void ReturnStmt.collectBranches(Collection c) {
055        c.add(this);
056      }
057      public void BranchPropagation.collectBranches(Collection c) {
058        c.addAll(escapedBranches());
059      }
060      public void TryStmt.collectBranches(Collection c) {
061        c.addAll(escapedBranches());
062      }
063      
064      syn boolean ContinueStmt.hasLabel() = !getLabel().equals("");
065      syn boolean BreakStmt.hasLabel() = !getLabel().equals("");
066    
067      eq LabeledStmt.targetOf(ContinueStmt stmt) =
068        stmt.hasLabel() && stmt.getLabel().equals(getLabel());
069      eq WhileStmt.targetOf(ContinueStmt stmt) = !stmt.hasLabel();
070      eq DoStmt.targetOf(ContinueStmt stmt) = !stmt.hasLabel();
071      eq ForStmt.targetOf(ContinueStmt stmt) = !stmt.hasLabel();
072      eq SwitchStmt.targetOf(ContinueStmt stmt) = false;
073      
074      eq LabeledStmt.targetOf(BreakStmt stmt) =
075        stmt.hasLabel() && stmt.getLabel().equals(getLabel());
076      eq SwitchStmt.targetOf(BreakStmt stmt) = !stmt.hasLabel();
077      eq WhileStmt.targetOf(BreakStmt stmt) = !stmt.hasLabel();
078      eq DoStmt.targetOf(BreakStmt stmt) = !stmt.hasLabel();
079      eq ForStmt.targetOf(BreakStmt stmt) = !stmt.hasLabel();
080      
081      // the branches for which this node is the target
082      eq BranchPropagation.targetBranches() {
083        HashSet set = new HashSet();
084        for(Iterator iter = branches().iterator(); iter.hasNext(); ) {
085          Object o = iter.next();
086          if(o instanceof ContinueStmt && targetOf((ContinueStmt)o))
087            set.add(o);
088          if(o instanceof BreakStmt && targetOf((BreakStmt)o))
089            set.add(o);
090        }
091        return set;
092      }
093      // the branches that escape this node
094      eq BranchPropagation.escapedBranches() {
095        HashSet set = new HashSet();
096        for(Iterator iter = branches().iterator(); iter.hasNext(); ) {
097          Object o = iter.next();
098          if(o instanceof ContinueStmt && !targetOf((ContinueStmt)o))
099            set.add(o);
100          if(o instanceof BreakStmt && !targetOf((BreakStmt)o))
101            set.add(o);
102          if(o instanceof ReturnStmt)
103            set.add(o);
104        }
105        return set;
106      }
107      // all branches that reach this node
108      eq BranchPropagation.branches() {
109        HashSet set = new HashSet();
110        super.collectBranches(set);
111        return set;
112      }
113    
114      // all branches that reach this node
115      syn lazy Collection TryStmt.branches() {
116        HashSet set = new HashSet();
117        getBlock().collectBranches(set);
118        for(int i = 0; i < getNumCatchClause(); i++)
119          getCatchClause(i).collectBranches(set);
120        return set;
121      }
122      
123      syn lazy Collection TryStmt.branchesFromFinally() {
124        HashSet set = new HashSet();
125        if(hasFinally())
126          getFinally().collectBranches(set);
127        return set;
128      }
129    
130      // all branches the this node is target for
131      syn lazy Collection TryStmt.targetBranches() {
132        HashSet set = new HashSet();
133        if(hasFinally() && !getFinally().canCompleteNormally())
134          set.addAll(branches());
135        return set;
136      }
137    
138      // all branches that escapes
139      syn lazy Collection TryStmt.escapedBranches() {
140        HashSet set = new HashSet();
141        if(hasFinally())
142          set.addAll(branchesFromFinally());
143        if(!hasFinally() || getFinally().canCompleteNormally())
144          set.addAll(branches());
145        return set;
146      }
147      // find the target statement for break and continue
148      eq BreakStmt.targetStmt() = branchTarget(this);
149      eq ContinueStmt.targetStmt() = branchTarget(this);
150      public Stmt ASTNode.branchTarget(Stmt branchStmt) {
151        if(getParent() != null)
152          return getParent().branchTarget(branchStmt);
153        else
154          return null;
155      }
156      public Stmt BranchPropagation.branchTarget(Stmt branchStmt) {
157        if(targetBranches().contains(branchStmt))
158          return this;
159        return super.branchTarget(branchStmt);
160      }
161      public Stmt TryStmt.branchTarget(Stmt branchStmt) {
162        if(targetBranches().contains(branchStmt))
163          return this;
164        return super.branchTarget(branchStmt);
165      }
166      
167      // lookup visible label
168      inh lazy LabeledStmt BreakStmt.lookupLabel(String name);
169      inh lazy LabeledStmt ContinueStmt.lookupLabel(String name);
170      inh lazy LabeledStmt LabeledStmt.lookupLabel(String name);
171      eq LabeledStmt.getStmt().lookupLabel(String name) = name.equals(getLabel()) ? this : lookupLabel(name);
172      eq Program.getChild().lookupLabel(String name) = null;
173      
174      // compute the list of finally blocks that are reached on the way to the target
175      eq BreakStmt.finallyList() {
176        ArrayList list = new ArrayList();
177        collectFinally(this, list);
178        return list;
179      }
180      eq ContinueStmt.finallyList() {
181        ArrayList list = new ArrayList();
182        collectFinally(this, list);
183        return list;
184      }
185      eq ReturnStmt.finallyList() {
186        ArrayList list = new ArrayList();
187        collectFinally(this, list);
188        return list;
189      }
190      public void ASTNode.collectFinally(Stmt branchStmt, ArrayList list) {
191        if(getParent() != null)
192          getParent().collectFinally(branchStmt, list);
193      }
194      public void BranchPropagation.collectFinally(Stmt branchStmt, ArrayList list) {
195        if(targetBranches().contains(branchStmt))
196          return;
197        super.collectFinally(branchStmt, list);
198      }
199      public void TryStmt.collectFinally(Stmt branchStmt, ArrayList list) {
200        if(hasFinally() && !branchesFromFinally().contains(branchStmt))
201          list.add(this);
202        if(targetBranches().contains(branchStmt))
203          return;
204        super.collectFinally(branchStmt, list);
205      }
206      public void SynchronizedStmt.collectFinally(Stmt branchStmt, ArrayList list) {
207        list.add(this);
208        super.collectFinally(branchStmt, list);
209      }
210      public void BodyDecl.collectFinally(Stmt branchStmt, ArrayList list) {
211        // terminate search if body declaration is reached
212      }
213    
214      // find the continue statements that have this node as actual target
215      eq BranchPropagation.targetContinues() {
216        HashSet set = new HashSet();
217        for(Iterator iter = targetBranches().iterator(); iter.hasNext(); ) {
218          Object o = iter.next();
219          if(o instanceof ContinueStmt)
220            set.add(o);
221        }
222        if(getParent() instanceof LabeledStmt) {
223          for(Iterator iter = ((LabeledStmt)getParent()).targetBranches().iterator(); iter.hasNext(); ) {
224            Object o = iter.next();
225            if(o instanceof ContinueStmt)
226              set.add(o);
227          }
228        }
229        return set;
230      }
231      // find the break statements that have this node as their actual target
232      eq BranchPropagation.targetBreaks() {
233        HashSet set = new HashSet();
234        for(Iterator iter = targetBranches().iterator(); iter.hasNext(); ) {
235          Object o = iter.next();
236          if(o instanceof BreakStmt)
237            set.add(o);
238        }
239        return set;
240      }
241    
242    }
243