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 jdk.nashorn.internal.codegen.types.Type; 29 import jdk.nashorn.internal.ir.annotations.Immutable; 30 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 31 import jdk.nashorn.internal.parser.TokenType; 32 33 /** 34 * TernaryNode represent the ternary operator {@code ?:}. Note that for control-flow calculation reasons its branch 35 * expressions (but not its test expression) are always wrapped in instances of {@link JoinPredecessorExpression}. 36 */ 37 @Immutable 38 public final class TernaryNode extends Expression { 39 private static final long serialVersionUID = 1L; 40 41 private final Expression test; 42 private final JoinPredecessorExpression trueExpr; 43 private final JoinPredecessorExpression falseExpr; 44 45 /** 46 * Constructor 47 * 48 * @param token token 49 * @param test test expression 50 * @param trueExpr expression evaluated when test evaluates to true 51 * @param falseExpr expression evaluated when test evaluates to true 52 */ 53 public TernaryNode(final long token, final Expression test, final JoinPredecessorExpression trueExpr, final JoinPredecessorExpression falseExpr) { 54 super(token, falseExpr.getFinish()); 55 this.test = test; 56 this.trueExpr = trueExpr; 57 this.falseExpr = falseExpr; 58 } 59 60 private TernaryNode(final TernaryNode ternaryNode, final Expression test, final JoinPredecessorExpression trueExpr, 61 final JoinPredecessorExpression falseExpr) { 62 super(ternaryNode); 63 this.test = test; 64 this.trueExpr = trueExpr; 65 this.falseExpr = falseExpr; 66 } 67 68 @Override 69 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 70 if (visitor.enterTernaryNode(this)) { 71 final Expression newTest = (Expression)getTest().accept(visitor); 72 final JoinPredecessorExpression newTrueExpr = (JoinPredecessorExpression)trueExpr.accept(visitor); 73 final JoinPredecessorExpression newFalseExpr = (JoinPredecessorExpression)falseExpr.accept(visitor); 74 return visitor.leaveTernaryNode(setTest(newTest).setTrueExpression(newTrueExpr).setFalseExpression(newFalseExpr)); 75 } 76 77 return this; 78 } 79 80 @Override 81 public void toString(final StringBuilder sb, final boolean printType) { 82 final TokenType tokenType = tokenType(); 83 final boolean testParen = tokenType.needsParens(getTest().tokenType(), true); 84 final boolean trueParen = tokenType.needsParens(getTrueExpression().tokenType(), false); 85 final boolean falseParen = tokenType.needsParens(getFalseExpression().tokenType(), false); 86 87 if (testParen) { 88 sb.append('('); 89 } 90 getTest().toString(sb, printType); 91 if (testParen) { 92 sb.append(')'); 93 } 94 95 sb.append(" ? "); 96 97 if (trueParen) { 98 sb.append('('); 99 } 100 getTrueExpression().toString(sb, printType); 101 if (trueParen) { 102 sb.append(')'); 103 } 104 105 sb.append(" : "); 106 107 if (falseParen) { 108 sb.append('('); 109 } 110 getFalseExpression().toString(sb, printType); 111 if (falseParen) { 112 sb.append(')'); 113 } 114 } 115 116 @Override 117 public boolean isLocal() { 118 return getTest().isLocal() 119 && getTrueExpression().isLocal() 120 && getFalseExpression().isLocal(); 121 } 122 123 @Override 124 public Type getType() { 125 return Type.widestReturnType(getTrueExpression().getType(), getFalseExpression().getType()); 126 } 127 128 129 /** 130 * Get the test expression for this ternary expression, i.e. "x" in x ? y : z 131 * @return the test expression 132 */ 133 public Expression getTest() { 134 return test; 135 } 136 137 /** 138 * Get the true expression for this ternary expression, i.e. "y" in x ? y : z 139 * @return the true expression 140 */ 141 public JoinPredecessorExpression getTrueExpression() { 142 return trueExpr; 143 } 144 145 /** 146 * Get the false expression for this ternary expression, i.e. "z" in x ? y : z 147 * @return the false expression 148 */ 149 public JoinPredecessorExpression getFalseExpression() { 150 return falseExpr; 151 } 152 153 /** 154 * Set the test expression for this node 155 * @param test new test expression 156 * @return a node equivalent to this one except for the requested change. 157 */ 158 public TernaryNode setTest(final Expression test) { 159 if (this.test == test) { 160 return this; 161 } 162 return new TernaryNode(this, test, trueExpr, falseExpr); 163 } 164 165 /** 166 * Set the true expression for this node 167 * @param trueExpr new true expression 168 * @return a node equivalent to this one except for the requested change. 169 */ 170 public TernaryNode setTrueExpression(final JoinPredecessorExpression trueExpr) { 171 if (this.trueExpr == trueExpr) { 172 return this; 173 } 174 return new TernaryNode(this, test, trueExpr, falseExpr); 175 } 176 177 /** 178 * Set the false expression for this node 179 * @param falseExpr new false expression 180 * @return a node equivalent to this one except for the requested change. 181 */ 182 public TernaryNode setFalseExpression(final JoinPredecessorExpression falseExpr) { 183 if (this.falseExpr == falseExpr) { 184 return this; 185 } 186 return new TernaryNode(this, test, trueExpr, falseExpr); 187 } 188 }