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  import jdk.nashorn.internal.codegen.Label;
32  import jdk.nashorn.internal.ir.annotations.Immutable;
33  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
34  
35  /**
36   * IR representation of a SWITCH statement.
37   */
38  @Immutable
39  public final class SwitchNode extends BreakableStatement {
40      /** Switch expression. */
41      private final Expression expression;
42  
43      /** Switch cases. */
44      private final List<CaseNode> cases;
45  
46      /** Switch default index. */
47      private final int defaultCaseIndex;
48  
49      /** Tag symbol. */
50      private Symbol tag;
51  
52      /**
53       * Constructor
54       *
55       * @param lineNumber  lineNumber
56       * @param token       token
57       * @param finish      finish
58       * @param expression  switch expression
59       * @param cases       cases
60       * @param defaultCase the default case node - null if none, otherwise has to be present in cases list
61       */
62      public SwitchNode(final int lineNumber, final long token, final int finish, final Expression expression, final List<CaseNode> cases, final CaseNode defaultCase) {
63          super(lineNumber, token, finish, new Label("switch_break"));
64          this.expression       = expression;
65          this.cases            = cases;
66          this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
67      }
68  
69      private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases, final int defaultCase) {
70          super(switchNode);
71          this.expression       = expression;
72          this.cases            = cases;
73          this.defaultCaseIndex = defaultCase;
74          this.tag              = switchNode.getTag(); //TODO are symbols inhereted as references?
75      }
76  
77      @Override
78      public Node ensureUniqueLabels(final LexicalContext lc) {
79          final List<CaseNode> newCases = new ArrayList<>();
80          for (final CaseNode caseNode : cases) {
81              newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
82          }
83          return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
84      }
85  
86      @Override
87      public boolean isTerminal() {
88          //there must be a default case, and that including all other cases must terminate
89          if (!cases.isEmpty() && defaultCaseIndex != -1) {
90              for (final CaseNode caseNode : cases) {
91                  if (!caseNode.isTerminal()) {
92                      return false;
93                  }
94              }
95              return true;
96          }
97          return false;
98  
99      }
100 
101     @Override
102     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
103         if (visitor.enterSwitchNode(this)) {
104             return visitor.leaveSwitchNode(
105                 setExpression(lc, (Expression)expression.accept(visitor)).
106                 setCases(lc, Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
107         }
108 
109         return this;
110     }
111 
112     @Override
113     public void toString(final StringBuilder sb) {
114         sb.append("switch (");
115         expression.toString(sb);
116         sb.append(')');
117     }
118 
119     /**
120      * Return the case node that is default case
121      * @return default case or null if none
122      */
123     public CaseNode getDefaultCase() {
124         return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
125     }
126 
127     /**
128      * Get the cases in this switch
129      * @return a list of case nodes
130      */
131     public List<CaseNode> getCases() {
132         return Collections.unmodifiableList(cases);
133     }
134 
135     /**
136      * Replace case nodes with new list. the cases have to be the same
137      * and the default case index the same. This is typically used
138      * by NodeVisitors who perform operations on every case node
139      * @param lc    lexical context
140      * @param cases list of cases
141      * @return new switcy node or same if no state was changed
142      */
143     public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
144         return setCases(lc, cases, defaultCaseIndex);
145     }
146 
147     private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
148         if (this.cases == cases) {
149             return this;
150         }
151         return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
152     }
153 
154     /**
155      * Set or reset the list of cases in this switch
156      * @param lc lexical context
157      * @param cases a list of cases, case nodes
158      * @param defaultCase a case in the list that is the default - must be in the list or class will assert
159      * @return new switch node or same if no state was changed
160      */
161     public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
162         return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
163     }
164 
165     /**
166      * Return the expression to switch on
167      * @return switch expression
168      */
169     public Expression getExpression() {
170         return expression;
171     }
172 
173     /**
174      * Set or reset the expression to switch on
175      * @param lc lexical context
176      * @param expression switch expression
177      * @return new switch node or same if no state was changed
178      */
179     public SwitchNode setExpression(final LexicalContext lc, final Expression expression) {
180         if (this.expression == expression) {
181             return this;
182         }
183         return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
184     }
185 
186     /**
187      * Get the tag symbol for this switch. The tag symbol is where
188      * the switch expression result is stored
189      * @return tag symbol
190      */
191     public Symbol getTag() {
192         return tag;
193     }
194 
195     /**
196      * Set the tag symbol for this switch. The tag symbol is where
197      * the switch expression result is stored
198      * @param tag a symbol
199      */
200     public void setTag(final Symbol tag) {
201         this.tag = tag;
202     }
203 }
204