TryNode.java revision 1068:34ef988d5959
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.List;
31import jdk.nashorn.internal.ir.annotations.Immutable;
32import jdk.nashorn.internal.ir.visitor.NodeVisitor;
33
34/**
35 * IR representation of a TRY statement.
36 */
37@Immutable
38public final class TryNode extends Statement implements JoinPredecessor {
39    private static final long serialVersionUID = 1L;
40
41    /** Try statements. */
42    private final Block body;
43
44    /** List of catch clauses. */
45    private final List<Block> catchBlocks;
46
47    /** Finally clause. */
48    private final Block finallyBody;
49
50    /** Exception symbol. */
51    private Symbol exception;
52
53    /** Catchall exception for finally expansion, where applicable */
54    private Symbol finallyCatchAll;
55
56    private final LocalVariableConversion conversion;
57
58    /**
59     * Constructor
60     *
61     * @param lineNumber  lineNumber
62     * @param token       token
63     * @param finish      finish
64     * @param body        try node body
65     * @param catchBlocks list of catch blocks in order
66     * @param finallyBody body of finally block or null if none
67     */
68    public TryNode(final int lineNumber, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
69        super(lineNumber, token, finish);
70        this.body        = body;
71        this.catchBlocks = catchBlocks;
72        this.finallyBody = finallyBody;
73        this.conversion  = null;
74    }
75
76    private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) {
77        super(tryNode);
78        this.body        = body;
79        this.catchBlocks = catchBlocks;
80        this.finallyBody = finallyBody;
81        this.conversion  = conversion;
82        this.exception = tryNode.exception;
83    }
84
85    @Override
86    public Node ensureUniqueLabels(final LexicalContext lc) {
87        //try nodes are never in lex context
88        return new TryNode(this, body, catchBlocks, finallyBody, conversion);
89    }
90
91    @Override
92    public boolean isTerminal() {
93        if (body.isTerminal()) {
94            for (final Block catchBlock : getCatchBlocks()) {
95                if (!catchBlock.isTerminal()) {
96                    return false;
97                }
98            }
99            return true;
100        }
101        return false;
102    }
103
104    /**
105     * Assist in IR navigation.
106     * @param visitor IR navigating visitor.
107     */
108    @Override
109    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
110        if (visitor.enterTryNode(this)) {
111            // Need to do finallybody first for termination analysis. TODO still necessary?
112            final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
113            final Block newBody        = (Block)body.accept(visitor);
114            return visitor.leaveTryNode(
115                setBody(newBody).
116                setFinallyBody(newFinallyBody).
117                setCatchBlocks(Node.accept(visitor, catchBlocks)).
118                setFinallyCatchAll(finallyCatchAll));
119        }
120
121        return this;
122    }
123
124    @Override
125    public void toString(final StringBuilder sb, final boolean printType) {
126        sb.append("try ");
127    }
128
129    /**
130     * Get the body for this try block
131     * @return body
132     */
133    public Block getBody() {
134        return body;
135    }
136
137    /**
138     * Reset the body of this try block
139     * @param body new body
140     * @return new TryNode or same if unchanged
141     */
142    public TryNode setBody(final Block body) {
143        if (this.body == body) {
144            return this;
145        }
146        return new TryNode(this,  body, catchBlocks, finallyBody, conversion);
147    }
148
149    /**
150     * Get the catches for this try block
151     * @return a list of catch nodes
152     */
153    public List<CatchNode> getCatches() {
154        final List<CatchNode> catches = new ArrayList<>(catchBlocks.size());
155        for (final Block catchBlock : catchBlocks) {
156            catches.add(getCatchNodeFromBlock(catchBlock));
157        }
158        return Collections.unmodifiableList(catches);
159    }
160
161    private static CatchNode getCatchNodeFromBlock(final Block catchBlock) {
162        return (CatchNode)catchBlock.getStatements().get(0);
163    }
164
165    /**
166     * Get the catch blocks for this try block
167     * @return a list of blocks
168     */
169    public List<Block> getCatchBlocks() {
170        return Collections.unmodifiableList(catchBlocks);
171    }
172
173    /**
174     * Set the catch blocks of this try
175     * @param catchBlocks list of catch blocks
176     * @return new TryNode or same if unchanged
177     */
178    public TryNode setCatchBlocks(final List<Block> catchBlocks) {
179        if (this.catchBlocks == catchBlocks) {
180            return this;
181        }
182        return new TryNode(this, body, catchBlocks, finallyBody, conversion);
183    }
184
185    /**
186     * Get the exception symbol for this try block
187     * @return a symbol for the compiler to store the exception in
188     */
189    public Symbol getException() {
190        return exception;
191    }
192    /**
193     * Set the exception symbol for this try block
194     * @param exception a symbol for the compiler to store the exception in
195     * @return new TryNode or same if unchanged
196     */
197    public TryNode setException(final Symbol exception) {
198        this.exception = exception;
199        return this;
200    }
201
202    /**
203     * Get the catch all symbol for this try block
204     * @return catch all symbol
205     */
206    public Symbol getFinallyCatchAll() {
207        return this.finallyCatchAll;
208    }
209
210    /**
211     * If a finally block exists, the synthetic catchall needs another symbol to
212     * store its throwable
213     * @param finallyCatchAll a symbol for the finally catch all exception
214     * @return new TryNode or same if unchanged
215     *
216     * TODO can this still be stateful?
217     */
218    public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
219        this.finallyCatchAll = finallyCatchAll;
220        return this;
221    }
222
223    /**
224     * Get the body of the finally clause for this try
225     * @return finally body, or null if no finally
226     */
227    public Block getFinallyBody() {
228        return finallyBody;
229    }
230
231    /**
232     * Set the finally body of this try
233     * @param finallyBody new finally body
234     * @return new TryNode or same if unchanged
235     */
236    public TryNode setFinallyBody(final Block finallyBody) {
237        if (this.finallyBody == finallyBody) {
238            return this;
239        }
240        return new TryNode(this, body, catchBlocks, finallyBody, conversion);
241    }
242
243    @Override
244    public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
245        if(this.conversion == conversion) {
246            return this;
247        }
248        return new TryNode(this, body, catchBlocks, finallyBody, conversion);
249    }
250
251    @Override
252    public LocalVariableConversion getLocalVariableConversion() {
253        return conversion;
254    }
255}
256