SwitchNode.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.List;
31import jdk.nashorn.internal.codegen.Label;
32import jdk.nashorn.internal.ir.annotations.Immutable;
33import jdk.nashorn.internal.ir.visitor.NodeVisitor;
34
35/**
36 * IR representation of a SWITCH statement.
37 */
38@Immutable
39public final class SwitchNode extends BreakableStatement {
40    private static final long serialVersionUID = 1L;
41
42    /** Switch expression. */
43    private final Expression expression;
44
45    /** Switch cases. */
46    private final List<CaseNode> cases;
47
48    /** Switch default index. */
49    private final int defaultCaseIndex;
50
51    /** True if all cases are 32-bit signed integer constants, without repetitions. It's a prerequisite for
52     * using a tableswitch/lookupswitch when generating code. */
53    private final boolean uniqueInteger;
54
55    /** Tag symbol. */
56    private final Symbol tag;
57
58    /**
59     * Constructor
60     *
61     * @param lineNumber  lineNumber
62     * @param token       token
63     * @param finish      finish
64     * @param expression  switch expression
65     * @param cases       cases
66     * @param defaultCase the default case node - null if none, otherwise has to be present in cases list
67     */
68    public SwitchNode(final int lineNumber, final long token, final int finish, final Expression expression, final List<CaseNode> cases, final CaseNode defaultCase) {
69        super(lineNumber, token, finish, new Label("switch_break"));
70        this.expression       = expression;
71        this.cases            = cases;
72        this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
73        this.uniqueInteger    = false;
74        this.tag = null;
75    }
76
77    private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases,
78            final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger, final Symbol tag) {
79        super(switchNode, conversion);
80        this.expression       = expression;
81        this.cases            = cases;
82        this.defaultCaseIndex = defaultCaseIndex;
83        this.tag              = tag;
84        this.uniqueInteger    = uniqueInteger;
85    }
86
87    @Override
88    public Node ensureUniqueLabels(final LexicalContext lc) {
89        final List<CaseNode> newCases = new ArrayList<>();
90        for (final CaseNode caseNode : cases) {
91            newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion()));
92        }
93        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger, tag));
94    }
95
96    @Override
97    public boolean isTerminal() {
98        //there must be a default case, and that including all other cases must terminate
99        if (!cases.isEmpty() && defaultCaseIndex != -1) {
100            for (final CaseNode caseNode : cases) {
101                if (!caseNode.isTerminal()) {
102                    return false;
103                }
104            }
105            return true;
106        }
107        return false;
108
109    }
110
111    @Override
112    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
113        if (visitor.enterSwitchNode(this)) {
114            return visitor.leaveSwitchNode(
115                setExpression(lc, (Expression)expression.accept(visitor)).
116                setCases(lc, Node.accept(visitor, cases), defaultCaseIndex));
117        }
118
119        return this;
120    }
121
122    @Override
123    public void toString(final StringBuilder sb, final boolean printType) {
124        sb.append("switch (");
125        expression.toString(sb, printType);
126        sb.append(')');
127    }
128
129    /**
130     * Return the case node that is default case
131     * @return default case or null if none
132     */
133    public CaseNode getDefaultCase() {
134        return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
135    }
136
137    /**
138     * Get the cases in this switch
139     * @return a list of case nodes
140     */
141    public List<CaseNode> getCases() {
142        return Collections.unmodifiableList(cases);
143    }
144
145    /**
146     * Replace case nodes with new list. the cases have to be the same
147     * and the default case index the same. This is typically used
148     * by NodeVisitors who perform operations on every case node
149     * @param lc    lexical context
150     * @param cases list of cases
151     * @return new switch node or same if no state was changed
152     */
153    public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
154        return setCases(lc, cases, defaultCaseIndex);
155    }
156
157    private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
158        if (this.cases == cases) {
159            return this;
160        }
161        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
162    }
163
164    /**
165     * Set or reset the list of cases in this switch
166     * @param lc lexical context
167     * @param cases a list of cases, case nodes
168     * @param defaultCase a case in the list that is the default - must be in the list or class will assert
169     * @return new switch node or same if no state was changed
170     */
171    public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
172        return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
173    }
174
175    /**
176     * Return the expression to switch on
177     * @return switch expression
178     */
179    public Expression getExpression() {
180        return expression;
181    }
182
183    /**
184     * Set or reset the expression to switch on
185     * @param lc lexical context
186     * @param expression switch expression
187     * @return new switch node or same if no state was changed
188     */
189    public SwitchNode setExpression(final LexicalContext lc, final Expression expression) {
190        if (this.expression == expression) {
191            return this;
192        }
193        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
194    }
195
196    /**
197     * Get the tag symbol for this switch. The tag symbol is where
198     * the switch expression result is stored
199     * @return tag symbol
200     */
201    public Symbol getTag() {
202        return tag;
203    }
204
205    /**
206     * Set the tag symbol for this switch. The tag symbol is where
207     * the switch expression result is stored
208     * @param lc lexical context
209     * @param tag a symbol
210     * @return a switch node with the symbol set
211     */
212    public SwitchNode setTag(final LexicalContext lc, final Symbol tag) {
213        if (this.tag == tag) {
214            return this;
215        }
216        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
217    }
218
219    /**
220     * Returns true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
221     * @return true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
222     */
223    public boolean isUniqueInteger() {
224        return uniqueInteger;
225    }
226
227    /**
228     * Sets whether all cases of this switch statement are 32-bit signed integer constants, without repetitions.
229     * @param lc lexical context
230     * @param uniqueInteger if true, all cases of this switch statement have been determined to be 32-bit signed
231     * integer constants, without repetitions.
232     * @return this switch node, if the value didn't change, or a new switch node with the changed value
233     */
234    public SwitchNode setUniqueInteger(final LexicalContext lc, final boolean uniqueInteger) {
235        if(this.uniqueInteger == uniqueInteger) {
236            return this;
237        }
238        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
239    }
240
241    @Override
242    JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
243        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger, tag));
244    }
245
246}
247