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