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