TryNode.java revision 1399:eea9202e8930
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.ir; 27 28import java.util.ArrayList; 29import java.util.Collections; 30import java.util.HashSet; 31import java.util.List; 32import java.util.Set; 33import jdk.nashorn.internal.ir.annotations.Immutable; 34import jdk.nashorn.internal.ir.visitor.NodeVisitor; 35 36/** 37 * IR representation of a TRY statement. 38 */ 39@Immutable 40public final class TryNode extends LexicalContextStatement implements JoinPredecessor { 41 private static final long serialVersionUID = 1L; 42 43 /** Try statements. */ 44 private final Block body; 45 46 /** List of catch clauses. */ 47 private final List<Block> catchBlocks; 48 49 /** Finally clause. */ 50 private final Block finallyBody; 51 52 /** 53 * List of inlined finally blocks. The structure of every inlined finally is: 54 * Block(LabelNode(label, Block(finally-statements, (JumpStatement|ReturnNode)?))). 55 * That is, the block has a single LabelNode statement with the label and a block containing the 56 * statements of the inlined finally block with the jump or return statement appended (if the finally 57 * block was not terminal; the original jump/return is simply ignored if the finally block itself 58 * terminates). The reason for this somewhat strange arrangement is that we didn't want to create a 59 * separate class for the (label, BlockStatement pair) but rather reused the already available LabelNode. 60 * However, if we simply used List<LabelNode> without wrapping the label nodes in an additional Block, 61 * that would've thrown off visitors relying on BlockLexicalContext -- same reason why we never use 62 * Statement as the type of bodies of e.g. IfNode, WhileNode etc. but rather blockify them even when they're 63 * single statements. 64 */ 65 private final List<Block> inlinedFinallies; 66 67 /** Exception symbol. */ 68 private final Symbol exception; 69 70 private final LocalVariableConversion conversion; 71 72 /** 73 * Constructor 74 * 75 * @param lineNumber lineNumber 76 * @param token token 77 * @param finish finish 78 * @param body try node body 79 * @param catchBlocks list of catch blocks in order 80 * @param finallyBody body of finally block or null if none 81 */ 82 public TryNode(final int lineNumber, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) { 83 super(lineNumber, token, finish); 84 this.body = body; 85 this.catchBlocks = catchBlocks; 86 this.finallyBody = finallyBody; 87 this.conversion = null; 88 this.inlinedFinallies = Collections.emptyList(); 89 this.exception = null; 90 } 91 92 private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies, final Symbol exception) { 93 super(tryNode); 94 this.body = body; 95 this.catchBlocks = catchBlocks; 96 this.finallyBody = finallyBody; 97 this.conversion = conversion; 98 this.inlinedFinallies = inlinedFinallies; 99 this.exception = exception; 100 } 101 102 @Override 103 public Node ensureUniqueLabels(final LexicalContext lc) { 104 //try nodes are never in lex context 105 return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); 106 } 107 108 @Override 109 public boolean isTerminal() { 110 if (body.isTerminal()) { 111 for (final Block catchBlock : getCatchBlocks()) { 112 if (!catchBlock.isTerminal()) { 113 return false; 114 } 115 } 116 return true; 117 } 118 return false; 119 } 120 121 /** 122 * Assist in IR navigation. 123 * @param visitor IR navigating visitor. 124 */ 125 @Override 126 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 127 if (visitor.enterTryNode(this)) { 128 // Need to do finallybody first for termination analysis. TODO still necessary? 129 final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor); 130 final Block newBody = (Block)body.accept(visitor); 131 return visitor.leaveTryNode( 132 setBody(lc, newBody). 133 setFinallyBody(lc, newFinallyBody). 134 setCatchBlocks(lc, Node.accept(visitor, catchBlocks)). 135 setInlinedFinallies(lc, Node.accept(visitor, inlinedFinallies))); 136 } 137 138 return this; 139 } 140 141 @Override 142 public void toString(final StringBuilder sb, final boolean printType) { 143 sb.append("try "); 144 } 145 146 /** 147 * Get the body for this try block 148 * @return body 149 */ 150 public Block getBody() { 151 return body; 152 } 153 154 /** 155 * Reset the body of this try block 156 * @param lc current lexical context 157 * @param body new body 158 * @return new TryNode or same if unchanged 159 */ 160 public TryNode setBody(final LexicalContext lc, final Block body) { 161 if (this.body == body) { 162 return this; 163 } 164 return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); 165 } 166 167 /** 168 * Get the catches for this try block 169 * @return a list of catch nodes 170 */ 171 public List<CatchNode> getCatches() { 172 final List<CatchNode> catches = new ArrayList<>(catchBlocks.size()); 173 for (final Block catchBlock : catchBlocks) { 174 catches.add(getCatchNodeFromBlock(catchBlock)); 175 } 176 return Collections.unmodifiableList(catches); 177 } 178 179 private static CatchNode getCatchNodeFromBlock(final Block catchBlock) { 180 return (CatchNode)catchBlock.getStatements().get(0); 181 } 182 183 /** 184 * Get the catch blocks for this try block 185 * @return a list of blocks 186 */ 187 public List<Block> getCatchBlocks() { 188 return Collections.unmodifiableList(catchBlocks); 189 } 190 191 /** 192 * Set the catch blocks of this try 193 * @param lc current lexical context 194 * @param catchBlocks list of catch blocks 195 * @return new TryNode or same if unchanged 196 */ 197 public TryNode setCatchBlocks(final LexicalContext lc, final List<Block> catchBlocks) { 198 if (this.catchBlocks == catchBlocks) { 199 return this; 200 } 201 return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); 202 } 203 204 /** 205 * Get the exception symbol for this try block 206 * @return a symbol for the compiler to store the exception in 207 */ 208 public Symbol getException() { 209 return exception; 210 } 211 /** 212 * Set the exception symbol for this try block 213 * @param lc lexical context 214 * @param exception a symbol for the compiler to store the exception in 215 * @return new TryNode or same if unchanged 216 */ 217 public TryNode setException(final LexicalContext lc, final Symbol exception) { 218 if (this.exception == exception) { 219 return this; 220 } 221 return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); 222 } 223 224 /** 225 * Get the body of the finally clause for this try 226 * @return finally body, or null if no finally 227 */ 228 public Block getFinallyBody() { 229 return finallyBody; 230 } 231 232 /** 233 * Get the inlined finally block with the given label name. This returns the actual finally block in the 234 * {@link LabelNode}, not the outer wrapper block for the {@link LabelNode}. 235 * @param labelName the name of the inlined finally's label 236 * @return the requested finally block, or null if no finally block's label matches the name. 237 */ 238 public Block getInlinedFinally(final String labelName) { 239 for(final Block inlinedFinally: inlinedFinallies) { 240 final LabelNode labelNode = getInlinedFinallyLabelNode(inlinedFinally); 241 if (labelNode.getLabelName().equals(labelName)) { 242 return labelNode.getBody(); 243 } 244 } 245 return null; 246 } 247 248 private static LabelNode getInlinedFinallyLabelNode(final Block inlinedFinally) { 249 return (LabelNode)inlinedFinally.getStatements().get(0); 250 } 251 252 /** 253 * Given an outer wrapper block for the {@link LabelNode} as returned by {@link #getInlinedFinallies()}, 254 * returns its actual inlined finally block. 255 * @param inlinedFinally the outer block for inlined finally, as returned as an element of 256 * {@link #getInlinedFinallies()}. 257 * @return the block contained in the {@link LabelNode} contained in the passed block. 258 */ 259 public static Block getLabelledInlinedFinallyBlock(final Block inlinedFinally) { 260 return getInlinedFinallyLabelNode(inlinedFinally).getBody(); 261 } 262 263 /** 264 * Returns a list of inlined finally blocks. Note that this returns a list of {@link Block}s such that each one of 265 * them has a single {@link LabelNode}, which in turn contains the label name for the finally block and the 266 * actual finally block. To safely extract the actual finally block, use 267 * {@link #getLabelledInlinedFinallyBlock(Block)}. 268 * @return a list of inlined finally blocks. 269 */ 270 public List<Block> getInlinedFinallies() { 271 return Collections.unmodifiableList(inlinedFinallies); 272 } 273 274 /** 275 * Set the finally body of this try 276 * @param lc current lexical context 277 * @param finallyBody new finally body 278 * @return new TryNode or same if unchanged 279 */ 280 public TryNode setFinallyBody(final LexicalContext lc, final Block finallyBody) { 281 if (this.finallyBody == finallyBody) { 282 return this; 283 } 284 return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); 285 } 286 287 /** 288 * Set the inlined finally blocks of this try. Each element should be a block with a single statement that is a 289 * {@link LabelNode} with a unique label, and the block within the label node should contain the actual inlined 290 * finally block. 291 * @param lc current lexical context 292 * @param inlinedFinallies list of inlined finally blocks 293 * @return new TryNode or same if unchanged 294 */ 295 public TryNode setInlinedFinallies(final LexicalContext lc, final List<Block> inlinedFinallies) { 296 if (this.inlinedFinallies == inlinedFinallies) { 297 return this; 298 } 299 assert checkInlinedFinallies(inlinedFinallies); 300 return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception)); 301 } 302 303 private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { 304 if (!inlinedFinallies.isEmpty()) { 305 final Set<String> labels = new HashSet<>(); 306 for (final Block inlinedFinally : inlinedFinallies) { 307 final List<Statement> stmts = inlinedFinally.getStatements(); 308 assert stmts.size() == 1; 309 final LabelNode ln = getInlinedFinallyLabelNode(inlinedFinally); 310 assert labels.add(ln.getLabelName()); // unique label 311 } 312 } 313 return true; 314 } 315 316 @Override 317 public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) { 318 if(this.conversion == conversion) { 319 return this; 320 } 321 return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies, exception); 322 } 323 324 @Override 325 public LocalVariableConversion getLocalVariableConversion() { 326 return conversion; 327 } 328} 329