author | attila |
Wed, 28 Jan 2015 17:58:08 +0100 | |
changeset 28690 | 78317797ab62 |
parent 27204 | 06ec78f29a56 |
child 30701 | 1a25e71187ff |
permissions | -rw-r--r-- |
16147 | 1 |
/* |
16151 | 2 |
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
16147 | 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 |
||
26 |
package jdk.nashorn.internal.ir; |
|
27 |
||
28 |
import java.util.ArrayList; |
|
29 |
import java.util.Collections; |
|
28690 | 30 |
import java.util.HashSet; |
16147 | 31 |
import java.util.List; |
28690 | 32 |
import java.util.Set; |
17233 | 33 |
import jdk.nashorn.internal.ir.annotations.Immutable; |
16147 | 34 |
import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
35 |
||
36 |
/** |
|
37 |
* IR representation of a TRY statement. |
|
38 |
*/ |
|
17233 | 39 |
@Immutable |
28690 | 40 |
public final class TryNode extends LexicalContextStatement implements JoinPredecessor { |
27204 | 41 |
private static final long serialVersionUID = 1L; |
42 |
||
16147 | 43 |
/** Try statements. */ |
17233 | 44 |
private final Block body; |
16147 | 45 |
|
46 |
/** List of catch clauses. */ |
|
17233 | 47 |
private final List<Block> catchBlocks; |
16147 | 48 |
|
49 |
/** Finally clause. */ |
|
17233 | 50 |
private final Block finallyBody; |
16147 | 51 |
|
28690 | 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 |
||
16147 | 67 |
/** Exception symbol. */ |
68 |
private Symbol exception; |
|
69 |
||
24751 | 70 |
private final LocalVariableConversion conversion; |
71 |
||
16147 | 72 |
/** |
73 |
* Constructor |
|
74 |
* |
|
17524
703643aeb0d6
8013914: Removed explicit LineNumberNodes that were too brittle when code moves around, and also introduced unnecessary footprint. Introduced the Statement node and fixed dead code elimination issues that were discovered by the absense of labels for LineNumberNodes.
lagergren
parents:
17523
diff
changeset
|
75 |
* @param lineNumber lineNumber |
17233 | 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 |
|
16147 | 81 |
*/ |
17524
703643aeb0d6
8013914: Removed explicit LineNumberNodes that were too brittle when code moves around, and also introduced unnecessary footprint. Introduced the Statement node and fixed dead code elimination issues that were discovered by the absense of labels for LineNumberNodes.
lagergren
parents:
17523
diff
changeset
|
82 |
public TryNode(final int lineNumber, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) { |
703643aeb0d6
8013914: Removed explicit LineNumberNodes that were too brittle when code moves around, and also introduced unnecessary footprint. Introduced the Statement node and fixed dead code elimination issues that were discovered by the absense of labels for LineNumberNodes.
lagergren
parents:
17523
diff
changeset
|
83 |
super(lineNumber, token, finish); |
703643aeb0d6
8013914: Removed explicit LineNumberNodes that were too brittle when code moves around, and also introduced unnecessary footprint. Introduced the Statement node and fixed dead code elimination issues that were discovered by the absense of labels for LineNumberNodes.
lagergren
parents:
17523
diff
changeset
|
84 |
this.body = body; |
17233 | 85 |
this.catchBlocks = catchBlocks; |
86 |
this.finallyBody = finallyBody; |
|
24751 | 87 |
this.conversion = null; |
28690 | 88 |
this.inlinedFinallies = Collections.emptyList(); |
16147 | 89 |
} |
90 |
||
28690 | 91 |
private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List<Block> inlinedFinallies) { |
16147 | 92 |
super(tryNode); |
17524
703643aeb0d6
8013914: Removed explicit LineNumberNodes that were too brittle when code moves around, and also introduced unnecessary footprint. Introduced the Statement node and fixed dead code elimination issues that were discovered by the absense of labels for LineNumberNodes.
lagergren
parents:
17523
diff
changeset
|
93 |
this.body = body; |
17233 | 94 |
this.catchBlocks = catchBlocks; |
95 |
this.finallyBody = finallyBody; |
|
24751 | 96 |
this.conversion = conversion; |
28690 | 97 |
this.inlinedFinallies = inlinedFinallies; |
24751 | 98 |
this.exception = tryNode.exception; |
16147 | 99 |
} |
100 |
||
101 |
@Override |
|
17233 | 102 |
public Node ensureUniqueLabels(final LexicalContext lc) { |
103 |
//try nodes are never in lex context |
|
28690 | 104 |
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); |
17233 | 105 |
} |
106 |
||
107 |
@Override |
|
108 |
public boolean isTerminal() { |
|
109 |
if (body.isTerminal()) { |
|
110 |
for (final Block catchBlock : getCatchBlocks()) { |
|
111 |
if (!catchBlock.isTerminal()) { |
|
112 |
return false; |
|
113 |
} |
|
114 |
} |
|
115 |
return true; |
|
116 |
} |
|
117 |
return false; |
|
16147 | 118 |
} |
119 |
||
120 |
/** |
|
121 |
* Assist in IR navigation. |
|
122 |
* @param visitor IR navigating visitor. |
|
123 |
*/ |
|
124 |
@Override |
|
28690 | 125 |
public Node accept(final LexicalContext lc, NodeVisitor<? extends LexicalContext> visitor) { |
17233 | 126 |
if (visitor.enterTryNode(this)) { |
127 |
// Need to do finallybody first for termination analysis. TODO still necessary? |
|
128 |
final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor); |
|
129 |
final Block newBody = (Block)body.accept(visitor); |
|
130 |
return visitor.leaveTryNode( |
|
28690 | 131 |
setBody(lc, newBody). |
132 |
setFinallyBody(lc, newFinallyBody). |
|
133 |
setCatchBlocks(lc, Node.accept(visitor, catchBlocks)). |
|
134 |
setInlinedFinallies(lc, Node.accept(visitor, inlinedFinallies))); |
|
16147 | 135 |
} |
136 |
||
137 |
return this; |
|
138 |
} |
|
139 |
||
140 |
@Override |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
141 |
public void toString(final StringBuilder sb, final boolean printType) { |
17233 | 142 |
sb.append("try "); |
16147 | 143 |
} |
144 |
||
145 |
/** |
|
146 |
* Get the body for this try block |
|
147 |
* @return body |
|
148 |
*/ |
|
149 |
public Block getBody() { |
|
150 |
return body; |
|
151 |
} |
|
152 |
||
153 |
/** |
|
154 |
* Reset the body of this try block |
|
28690 | 155 |
* @param lc current lexical context |
16147 | 156 |
* @param body new body |
17233 | 157 |
* @return new TryNode or same if unchanged |
16147 | 158 |
*/ |
28690 | 159 |
public TryNode setBody(final LexicalContext lc, final Block body) { |
17233 | 160 |
if (this.body == body) { |
161 |
return this; |
|
162 |
} |
|
28690 | 163 |
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); |
16147 | 164 |
} |
165 |
||
166 |
/** |
|
167 |
* Get the catches for this try block |
|
168 |
* @return a list of catch nodes |
|
169 |
*/ |
|
170 |
public List<CatchNode> getCatches() { |
|
171 |
final List<CatchNode> catches = new ArrayList<>(catchBlocks.size()); |
|
172 |
for (final Block catchBlock : catchBlocks) { |
|
24751 | 173 |
catches.add(getCatchNodeFromBlock(catchBlock)); |
16147 | 174 |
} |
17233 | 175 |
return Collections.unmodifiableList(catches); |
16530
201d682e75f4
8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
attila
parents:
16240
diff
changeset
|
176 |
} |
201d682e75f4
8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
attila
parents:
16240
diff
changeset
|
177 |
|
24751 | 178 |
private static CatchNode getCatchNodeFromBlock(final Block catchBlock) { |
179 |
return (CatchNode)catchBlock.getStatements().get(0); |
|
180 |
} |
|
181 |
||
16530
201d682e75f4
8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
attila
parents:
16240
diff
changeset
|
182 |
/** |
16147 | 183 |
* Get the catch blocks for this try block |
184 |
* @return a list of blocks |
|
185 |
*/ |
|
186 |
public List<Block> getCatchBlocks() { |
|
187 |
return Collections.unmodifiableList(catchBlocks); |
|
188 |
} |
|
189 |
||
190 |
/** |
|
191 |
* Set the catch blocks of this try |
|
28690 | 192 |
* @param lc current lexical context |
16147 | 193 |
* @param catchBlocks list of catch blocks |
17233 | 194 |
* @return new TryNode or same if unchanged |
16147 | 195 |
*/ |
28690 | 196 |
public TryNode setCatchBlocks(final LexicalContext lc, final List<Block> catchBlocks) { |
17233 | 197 |
if (this.catchBlocks == catchBlocks) { |
198 |
return this; |
|
199 |
} |
|
28690 | 200 |
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); |
16147 | 201 |
} |
202 |
||
203 |
/** |
|
204 |
* Get the exception symbol for this try block |
|
205 |
* @return a symbol for the compiler to store the exception in |
|
206 |
*/ |
|
207 |
public Symbol getException() { |
|
208 |
return exception; |
|
209 |
} |
|
210 |
/** |
|
211 |
* Set the exception symbol for this try block |
|
212 |
* @param exception a symbol for the compiler to store the exception in |
|
17233 | 213 |
* @return new TryNode or same if unchanged |
16147 | 214 |
*/ |
17233 | 215 |
public TryNode setException(final Symbol exception) { |
16147 | 216 |
this.exception = exception; |
17233 | 217 |
return this; |
16147 | 218 |
} |
219 |
||
220 |
/** |
|
221 |
* Get the body of the finally clause for this try |
|
222 |
* @return finally body, or null if no finally |
|
223 |
*/ |
|
224 |
public Block getFinallyBody() { |
|
225 |
return finallyBody; |
|
226 |
} |
|
227 |
||
228 |
/** |
|
28690 | 229 |
* Get the inlined finally block with the given label name. This returns the actual finally block in the |
230 |
* {@link LabelNode}, not the outer wrapper block for the {@link LabelNode}. |
|
231 |
* @param labelName the name of the inlined finally's label |
|
232 |
* @return the requested finally block, or null if no finally block's label matches the name. |
|
233 |
*/ |
|
234 |
public Block getInlinedFinally(final String labelName) { |
|
235 |
for(final Block inlinedFinally: inlinedFinallies) { |
|
236 |
final LabelNode labelNode = getInlinedFinallyLabelNode(inlinedFinally); |
|
237 |
if (labelNode.getLabelName().equals(labelName)) { |
|
238 |
return labelNode.getBody(); |
|
239 |
} |
|
240 |
} |
|
241 |
return null; |
|
242 |
} |
|
243 |
||
244 |
private static LabelNode getInlinedFinallyLabelNode(final Block inlinedFinally) { |
|
245 |
return (LabelNode)inlinedFinally.getStatements().get(0); |
|
246 |
} |
|
247 |
||
248 |
/** |
|
249 |
* Given an outer wrapper block for the {@link LabelNode} as returned by {@link #getInlinedFinallies()}, |
|
250 |
* returns its actual inlined finally block. |
|
251 |
* @param inlinedFinally the outer block for inlined finally, as returned as an element of |
|
252 |
* {@link #getInlinedFinallies()}. |
|
253 |
* @return the block contained in the {@link LabelNode} contained in the passed block. |
|
254 |
*/ |
|
255 |
public static Block getLabelledInlinedFinallyBlock(final Block inlinedFinally) { |
|
256 |
return getInlinedFinallyLabelNode(inlinedFinally).getBody(); |
|
257 |
} |
|
258 |
||
259 |
/** |
|
260 |
* Returns a list of inlined finally blocks. Note that this returns a list of {@link Block}s such that each one of |
|
261 |
* them has a single {@link LabelNode}, which in turn contains the label name for the finally block and the |
|
262 |
* actual finally block. To safely extract the actual finally block, use |
|
263 |
* {@link #getLabelledInlinedFinallyBlock(Block)}. |
|
264 |
* @return a list of inlined finally blocks. |
|
265 |
*/ |
|
266 |
public List<Block> getInlinedFinallies() { |
|
267 |
return Collections.unmodifiableList(inlinedFinallies); |
|
268 |
} |
|
269 |
||
270 |
/** |
|
16147 | 271 |
* Set the finally body of this try |
28690 | 272 |
* @param lc current lexical context |
16147 | 273 |
* @param finallyBody new finally body |
17233 | 274 |
* @return new TryNode or same if unchanged |
16147 | 275 |
*/ |
28690 | 276 |
public TryNode setFinallyBody(final LexicalContext lc, final Block finallyBody) { |
17233 | 277 |
if (this.finallyBody == finallyBody) { |
278 |
return this; |
|
279 |
} |
|
28690 | 280 |
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); |
281 |
} |
|
282 |
||
283 |
/** |
|
284 |
* Set the inlined finally blocks of this try. Each element should be a block with a single statement that is a |
|
285 |
* {@link LabelNode} with a unique label, and the block within the label node should contain the actual inlined |
|
286 |
* finally block. |
|
287 |
* @param lc current lexical context |
|
288 |
* @param inlinedFinallies list of inlined finally blocks |
|
289 |
* @return new TryNode or same if unchanged |
|
290 |
*/ |
|
291 |
public TryNode setInlinedFinallies(final LexicalContext lc, final List<Block> inlinedFinallies) { |
|
292 |
if (this.inlinedFinallies == inlinedFinallies) { |
|
293 |
return this; |
|
294 |
} |
|
295 |
assert checkInlinedFinallies(inlinedFinallies); |
|
296 |
return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); |
|
297 |
} |
|
298 |
||
299 |
private static boolean checkInlinedFinallies(final List<Block> inlinedFinallies) { |
|
300 |
if (!inlinedFinallies.isEmpty()) { |
|
301 |
final Set<String> labels = new HashSet<>(); |
|
302 |
for (final Block inlinedFinally : inlinedFinallies) { |
|
303 |
final List<Statement> stmts = inlinedFinally.getStatements(); |
|
304 |
assert stmts.size() == 1; |
|
305 |
final LabelNode ln = getInlinedFinallyLabelNode(inlinedFinally); |
|
306 |
assert labels.add(ln.getLabelName()); // unique label |
|
307 |
} |
|
308 |
} |
|
309 |
return true; |
|
24751 | 310 |
} |
311 |
||
312 |
@Override |
|
313 |
public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) { |
|
314 |
if(this.conversion == conversion) { |
|
315 |
return this; |
|
316 |
} |
|
28690 | 317 |
return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); |
24751 | 318 |
} |
319 |
||
320 |
@Override |
|
321 |
public LocalVariableConversion getLocalVariableConversion() { |
|
322 |
return conversion; |
|
16147 | 323 |
} |
324 |
} |