View Javadoc
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  
26  package jdk.nashorn.internal.ir;
27  
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.List;
31  
32  import jdk.nashorn.internal.codegen.Label;
33  import jdk.nashorn.internal.ir.annotations.Immutable;
34  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
35  
36  /**
37   * IR representation of a TRY statement.
38   */
39  @Immutable
40  public final class TryNode extends Statement {
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      /** Exit label. */
51      private final Label exit;
52  
53      /** Exception symbol. */
54      private Symbol exception;
55  
56      /** Catchall exception for finally expansion, where applicable */
57      private Symbol finallyCatchAll;
58  
59      /**
60       * Constructor
61       *
62       * @param lineNumber  lineNumber
63       * @param token       token
64       * @param finish      finish
65       * @param body        try node body
66       * @param catchBlocks list of catch blocks in order
67       * @param finallyBody body of finally block or null if none
68       */
69      public TryNode(final int lineNumber, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
70          super(lineNumber, token, finish);
71          this.body        = body;
72          this.catchBlocks = catchBlocks;
73          this.finallyBody = finallyBody;
74          this.exit        = new Label("exit");
75      }
76  
77      private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
78          super(tryNode);
79          this.body        = body;
80          this.catchBlocks = catchBlocks;
81          this.finallyBody = finallyBody;
82          this.exit        = new Label(tryNode.exit);
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);
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, Block.class, catchBlocks)).
118                 setException(exception).
119                 setFinallyCatchAll(finallyCatchAll));
120         }
121 
122         return this;
123     }
124 
125     @Override
126     public void toString(final StringBuilder sb) {
127         sb.append("try ");
128     }
129 
130     /**
131      * Get the body for this try block
132      * @return body
133      */
134     public Block getBody() {
135         return body;
136     }
137 
138     /**
139      * Reset the body of this try block
140      * @param body new body
141      * @return new TryNode or same if unchanged
142      */
143     public TryNode setBody(final Block body) {
144         if (this.body == body) {
145             return this;
146         }
147         return new TryNode(this,  body, catchBlocks, finallyBody);
148     }
149 
150     /**
151      * Get the catches for this try block
152      * @return a list of catch nodes
153      */
154     public List<CatchNode> getCatches() {
155         final List<CatchNode> catches = new ArrayList<>(catchBlocks.size());
156         for (final Block catchBlock : catchBlocks) {
157             catches.add((CatchNode)catchBlock.getStatements().get(0));
158         }
159         return Collections.unmodifiableList(catches);
160     }
161 
162     /**
163      * Get the catch blocks for this try block
164      * @return a list of blocks
165      */
166     public List<Block> getCatchBlocks() {
167         return Collections.unmodifiableList(catchBlocks);
168     }
169 
170     /**
171      * Set the catch blocks of this try
172      * @param catchBlocks list of catch blocks
173      * @return new TryNode or same if unchanged
174      */
175     public TryNode setCatchBlocks(final List<Block> catchBlocks) {
176         if (this.catchBlocks == catchBlocks) {
177             return this;
178         }
179         return new TryNode(this, body, catchBlocks, finallyBody);
180     }
181 
182     /**
183      * Get the exception symbol for this try block
184      * @return a symbol for the compiler to store the exception in
185      */
186     public Symbol getException() {
187         return exception;
188     }
189 
190     /**
191      * Set the exception symbol for this try block
192      * @param exception a symbol for the compiler to store the exception in
193      * @return new TryNode or same if unchanged
194      */
195     public TryNode setException(final Symbol exception) {
196         this.exception = exception;
197         return this;
198     }
199 
200     /**
201      * Get the catch all symbol for this try block
202      * @return catch all symbol
203      */
204     public Symbol getFinallyCatchAll() {
205         return this.finallyCatchAll;
206     }
207 
208     /**
209      * If a finally block exists, the synthetic catchall needs another symbol to
210      * store its throwable
211      * @param finallyCatchAll a symbol for the finally catch all exception
212      * @return new TryNode or same if unchanged
213      *
214      * TODO can this still be stateful?
215      */
216     public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
217         this.finallyCatchAll = finallyCatchAll;
218         return this;
219     }
220 
221     /**
222      * Get the exit label for this try block
223      * @return exit label
224      */
225     public Label getExit() {
226         return exit;
227     }
228 
229     /**
230      * Get the body of the finally clause for this try
231      * @return finally body, or null if no finally
232      */
233     public Block getFinallyBody() {
234         return finallyBody;
235     }
236 
237     /**
238      * Set the finally body of this try
239      * @param finallyBody new finally body
240      * @return new TryNode or same if unchanged
241      */
242     public TryNode setFinallyBody(final Block finallyBody) {
243         if (this.finallyBody == finallyBody) {
244             return this;
245         }
246         return new TryNode(this, body, catchBlocks, finallyBody);
247     }
248 }