001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *                    2013, Jesper Öqvist <jesper.oqvist@cs.lth.se>
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain the above copyright notice,
009     * this list of conditions and the following disclaimer.
010     *
011     * 2. Redistributions in binary form must reproduce the above copyright notice,
012     * this list of conditions and the following disclaimer in the documentation
013     * and/or other materials provided with the distribution.
014     *
015     * 3. Neither the name of the copyright holder nor the names of its
016     * contributors may be used to endorse or promote products derived from this
017     * software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    import java.util.HashSet;
033    
034    aspect BranchTarget {
035    
036      /**
037       * Find the target statement for break and continue. This can be a try
038       * statement with a finally block that can not complete normally.
039       */
040      syn lazy Stmt BreakStmt.targetStmt();
041    
042      /**
043       * Find the target statement for break and continue. This can be a try
044       * statement with a finally block that can not complete normally.
045       */
046      syn lazy Stmt ContinueStmt.targetStmt();
047    
048      class LazyFinallyIterator implements Iterator<FinallyHost> {
049        private final Stmt branch;
050        private FinallyHost enclosing;
051    
052        LazyFinallyIterator(Stmt root) {
053          branch = root;
054          enclosing = branch.enclosingFinally(branch);
055        }
056    
057        @Override
058        public boolean hasNext() {
059          return enclosing != null;
060        }
061    
062        @Override
063        public FinallyHost next() {
064          FinallyHost b = enclosing;
065          enclosing = enclosing.enclosingFinally(branch);
066          return b;
067        }
068    
069        @Override
070        public void remove() {
071          throw new UnsupportedOperationException();
072        }
073      }
074    
075      /**
076       * Finds enclosing finally and monitor exit blocks.
077       * @return an iterator for finally (and monitor exit) blocks that are
078       * reached before the final target of this statement is reached
079       */
080      syn lazy Iterator<FinallyHost> Stmt.finallyIterator() {
081        return new LazyFinallyIterator(this);
082      }
083    
084      syn lazy Collection<Stmt> BranchTargetStmt.targetContinues();
085      syn lazy Collection<Stmt> BranchTargetStmt.targetBreaks();
086      syn lazy Collection<Stmt> BranchTargetStmt.targetBranches();
087      syn lazy Collection<Stmt> BranchTargetStmt.escapedBranches();
088      syn lazy Collection<Stmt> BranchTargetStmt.branches();
089    
090      public void ASTNode.collectBranches(Collection<Stmt> c) {
091        for (int i = 0; i < getNumChild(); i++) {
092          getChild(i).collectBranches(c);
093        }
094      }
095    
096      public void ContinueStmt.collectBranches(Collection<Stmt> c) {
097        c.add(this);
098      }
099    
100      public void BreakStmt.collectBranches(Collection<Stmt> c) {
101        c.add(this);
102      }
103    
104      public void ReturnStmt.collectBranches(Collection<Stmt> c) {
105        c.add(this);
106      }
107    
108      public void BranchTargetStmt.collectBranches(Collection<Stmt> c) {
109        c.addAll(escapedBranches());
110      }
111    
112      public void TryStmt.collectBranches(Collection<Stmt> c) {
113        c.addAll(escapedBranches());
114      }
115    
116      syn boolean ContinueStmt.hasLabel() = !getLabel().equals("");
117      syn boolean BreakStmt.hasLabel() = !getLabel().equals("");
118    
119      /**
120       * The branches for which this node is the target.
121       */
122      eq BranchTargetStmt.targetBranches() {
123        Collection<Stmt> set = new HashSet<Stmt>();
124        for (Stmt branch : branches()) {
125          if (potentialTargetOf(branch)) {
126            set.add(branch);
127          }
128        }
129        return set;
130      }
131    
132      /**
133       * The branches that escape this node.
134       */
135      eq BranchTargetStmt.escapedBranches() {
136        Collection<Stmt> set = new HashSet<Stmt>();
137        for (Stmt branch : branches()) {
138          if (!potentialTargetOf(branch)) {
139            set.add(branch);
140          } else if (branch instanceof ReturnStmt) {
141            set.add(branch);
142          }
143        }
144        return set;
145      }
146    
147      /**
148       * All branches that reach this node.
149       */
150      eq BranchTargetStmt.branches() {
151        Collection<Stmt> set = new HashSet<Stmt>();
152        super.collectBranches(set);
153        return set;
154      }
155    
156      /**
157       * All branches that reach this node.
158       */
159      syn lazy Collection<Stmt> TryStmt.branches() {
160        Collection<Stmt> set = new HashSet<Stmt>();
161        getBlock().collectBranches(set);
162        for (int i = 0; i < getNumCatchClause(); i++) {
163          getCatchClause(i).collectBranches(set);
164        }
165        return set;
166      }
167    
168      /**
169       * All branches that escape this node
170       */
171      syn lazy Collection<Stmt> TryStmt.escapedBranches() {
172        Collection<Stmt> set = new HashSet<Stmt>();
173        if (hasNonEmptyFinally()) {
174          // branches from finally
175          getFinally().collectBranches(set);
176        }
177        if (!hasFinally() || getFinally().canCompleteNormally()) {
178          set.addAll(branches());
179        }
180        return set;
181      }
182    
183      /**
184       * @return <code>true</code> if this statement can branch to
185       * the target statement.
186       */
187      syn boolean Stmt.canBranchTo(BranchTargetStmt target) = false;
188      syn boolean Stmt.canBranchTo(LabeledStmt target) = false;
189      syn boolean Stmt.canBranchTo(SwitchStmt target) = false;
190    
191      eq BreakStmt.canBranchTo(BranchTargetStmt target) = !hasLabel();
192    
193      /**
194       * A break-statement can target a labeled statement if
195       * it has a label and the labels match.
196       */
197      eq BreakStmt.canBranchTo(LabeledStmt target) =
198          hasLabel() && target.getLabel().equals(getLabel());
199    
200      eq BreakStmt.canBranchTo(SwitchStmt target) = !hasLabel();
201    
202      eq ContinueStmt.canBranchTo(BranchTargetStmt target) = !hasLabel();
203    
204      eq ContinueStmt.canBranchTo(LabeledStmt target) =
205          hasLabel() && target.getLabel().equals(getLabel());
206    
207      eq ContinueStmt.canBranchTo(SwitchStmt target) = false;
208    
209      // find the target statement for break and continue
210      eq BreakStmt.targetStmt() = branchTarget(this);
211      eq ContinueStmt.targetStmt() = branchTarget(this);
212    
213      /**
214       * @return <code>true</code> if this statement is a potential
215       * branch target of the given branch statement.
216       */
217      syn boolean BranchTargetStmt.potentialTargetOf(Stmt branch) = false;
218      eq LabeledStmt.potentialTargetOf(Stmt branch) = branch.canBranchTo(this);
219      eq WhileStmt.potentialTargetOf(Stmt branch) = branch.canBranchTo(this);
220      eq DoStmt.potentialTargetOf(Stmt branch) = branch.canBranchTo(this);
221      eq ForStmt.potentialTargetOf(Stmt branch) = branch.canBranchTo(this);
222      eq SwitchStmt.potentialTargetOf(Stmt branch) = branch.canBranchTo(this);
223    
224      /**
225       * @return the target statement for a break or continue
226       */
227      inh Stmt Stmt.branchTarget(Stmt branch);
228    
229      eq BodyDecl.getChild().branchTarget(Stmt branch) {
230        throw new Error("Found no branch targets for " + branch.getClass().getName());
231      }
232    
233      eq LabeledStmt.getChild().branchTarget(Stmt branch) =
234          branch.canBranchTo(this) ? this : branchTarget(branch);
235    
236      eq WhileStmt.getChild().branchTarget(Stmt branch) =
237          branch.canBranchTo(this) ? this : branchTarget(branch);
238    
239      eq DoStmt.getChild().branchTarget(Stmt branch) =
240          branch.canBranchTo(this) ? this : branchTarget(branch);
241    
242      eq ForStmt.getChild().branchTarget(Stmt branch) =
243          branch.canBranchTo(this) ? this : branchTarget(branch);
244    
245      eq SwitchStmt.getChild().branchTarget(Stmt branch) =
246          branch.canBranchTo(this) ? this : branchTarget(branch);
247    
248      // lookup visible label
249      inh lazy LabeledStmt BreakStmt.lookupLabel(String name);
250      inh lazy LabeledStmt ContinueStmt.lookupLabel(String name);
251      inh lazy LabeledStmt LabeledStmt.lookupLabel(String name);
252      eq LabeledStmt.getStmt().lookupLabel(String name) = name.equals(getLabel()) ? this : lookupLabel(name);
253      eq Program.getChild().lookupLabel(String name) = null;
254    
255      /**
256       * Find finally block of enclosing try-statement, or monitor exit
257       * block of enclosing synchronized block.
258       * @param branch the source branch
259       * @return a finally block, or <code>null</code> if there is no
260       * enclosing try-statement
261       */
262      inh lazy FinallyHost Stmt.enclosingFinally(Stmt branch);
263    
264      eq BodyDecl.getChild().enclosingFinally(Stmt branch) = null;
265    
266      eq TryStmt.getChild().enclosingFinally(Stmt branch) =
267          hasNonEmptyFinally() ? this : enclosingFinally(branch);
268    
269      eq TryStmt.getFinally().enclosingFinally(Stmt branch) = enclosingFinally(branch);
270    
271      eq SynchronizedStmt.getChild().enclosingFinally(Stmt branch) = this;
272    
273      eq SynchronizedStmt.getMonitorExit().enclosingFinally(Stmt branch) = enclosingFinally(branch);
274    
275      eq BranchTargetStmt.getChild().enclosingFinally(Stmt branch) =
276          potentialTargetOf(branch) ? null : enclosingFinally(branch);
277    
278      /**
279       * NTA finally blocks ignore their immediate enclosing finally block
280       * (otherwise nta-finally blocks would be recursive).
281       */
282      eq NTAFinallyBlock.getChild().enclosingFinally(Stmt branch) =
283          origin.enclosingFinally(branch);
284    
285      // find the continue statements that have this node as actual target
286      eq BranchTargetStmt.targetContinues() {
287        HashSet<Stmt> set = new HashSet<Stmt>();
288        for (Stmt branch : targetBranches()) {
289          if (branch instanceof ContinueStmt) {
290            set.add(branch);
291          }
292        }
293        if (getParent() instanceof LabeledStmt) {
294          for (Stmt branch : ((LabeledStmt) getParent()).targetBranches()) {
295            if (branch instanceof ContinueStmt) {
296              set.add(branch);
297            }
298          }
299        }
300        return set;
301      }
302    
303      // find the break statements that have this node as their actual target
304      eq BranchTargetStmt.targetBreaks() {
305        HashSet<Stmt> set = new HashSet<Stmt>();
306        for (Stmt branch : targetBranches()) {
307          if (branch instanceof BreakStmt) {
308            set.add(branch);
309          }
310        }
311        return set;
312      }
313    
314    }
315