SwitchNode.java revision 953:221a84ef44c0
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 /** 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, 70 final int defaultCaseIndex, final LocalVariableConversion conversion) { 71 super(switchNode, conversion); 72 this.expression = expression; 73 this.cases = cases; 74 this.defaultCaseIndex = defaultCaseIndex; 75 this.tag = switchNode.getTag(); //TODO are symbols inhereted as references? 76 } 77 78 @Override 79 public Node ensureUniqueLabels(final LexicalContext lc) { 80 final List<CaseNode> newCases = new ArrayList<>(); 81 for (final CaseNode caseNode : cases) { 82 newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion())); 83 } 84 return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion)); 85 } 86 87 @Override 88 public boolean isTerminal() { 89 //there must be a default case, and that including all other cases must terminate 90 if (!cases.isEmpty() && defaultCaseIndex != -1) { 91 for (final CaseNode caseNode : cases) { 92 if (!caseNode.isTerminal()) { 93 return false; 94 } 95 } 96 return true; 97 } 98 return false; 99 100 } 101 102 @Override 103 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 104 if (visitor.enterSwitchNode(this)) { 105 return visitor.leaveSwitchNode( 106 setExpression(lc, (Expression)expression.accept(visitor)). 107 setCases(lc, Node.accept(visitor, cases), defaultCaseIndex)); 108 } 109 110 return this; 111 } 112 113 @Override 114 public void toString(final StringBuilder sb, final boolean printType) { 115 sb.append("switch ("); 116 expression.toString(sb, printType); 117 sb.append(')'); 118 } 119 120 /** 121 * Return the case node that is default case 122 * @return default case or null if none 123 */ 124 public CaseNode getDefaultCase() { 125 return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex); 126 } 127 128 /** 129 * Get the cases in this switch 130 * @return a list of case nodes 131 */ 132 public List<CaseNode> getCases() { 133 return Collections.unmodifiableList(cases); 134 } 135 136 /** 137 * Replace case nodes with new list. the cases have to be the same 138 * and the default case index the same. This is typically used 139 * by NodeVisitors who perform operations on every case node 140 * @param lc lexical context 141 * @param cases list of cases 142 * @return new switch node or same if no state was changed 143 */ 144 public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) { 145 return setCases(lc, cases, defaultCaseIndex); 146 } 147 148 private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) { 149 if (this.cases == cases) { 150 return this; 151 } 152 return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); 153 } 154 155 /** 156 * Set or reset the list of cases in this switch 157 * @param lc lexical context 158 * @param cases a list of cases, case nodes 159 * @param defaultCase a case in the list that is the default - must be in the list or class will assert 160 * @return new switch node or same if no state was changed 161 */ 162 public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) { 163 return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase)); 164 } 165 166 /** 167 * Return the expression to switch on 168 * @return switch expression 169 */ 170 public Expression getExpression() { 171 return expression; 172 } 173 174 /** 175 * Set or reset the expression to switch on 176 * @param lc lexical context 177 * @param expression switch expression 178 * @return new switch node or same if no state was changed 179 */ 180 public SwitchNode setExpression(final LexicalContext lc, final Expression expression) { 181 if (this.expression == expression) { 182 return this; 183 } 184 return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); 185 } 186 187 /** 188 * Get the tag symbol for this switch. The tag symbol is where 189 * the switch expression result is stored 190 * @return tag symbol 191 */ 192 public Symbol getTag() { 193 return tag; 194 } 195 196 /** 197 * Set the tag symbol for this switch. The tag symbol is where 198 * the switch expression result is stored 199 * @param tag a symbol 200 */ 201 public void setTag(final Symbol tag) { 202 this.tag = tag; 203 } 204 205 /** 206 * Returns true if all cases of this switch statement are 32-bit signed integer constants. 207 * @return true if all cases of this switch statement are 32-bit signed integer constants. 208 */ 209 public boolean isInteger() { 210 for (final CaseNode caseNode : cases) { 211 final Expression test = caseNode.getTest(); 212 if (test != null && !isIntegerLiteral(test)) { 213 return false; 214 } 215 } 216 return true; 217 } 218 219 @Override 220 JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { 221 return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion)); 222 } 223 224 private static boolean isIntegerLiteral(final Expression expr) { 225 return expr instanceof LiteralNode && ((LiteralNode<?>)expr).getValue() instanceof Integer; 226 } 227} 228