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 static jdk.nashorn.internal.parser.TokenType.BIT_NOT; 29import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; 30import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; 31import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 32 33import java.util.Arrays; 34import java.util.Collections; 35import java.util.List; 36import jdk.nashorn.internal.codegen.types.Type; 37import jdk.nashorn.internal.ir.annotations.Ignore; 38import jdk.nashorn.internal.ir.annotations.Immutable; 39import jdk.nashorn.internal.ir.visitor.NodeVisitor; 40import jdk.nashorn.internal.parser.Token; 41import jdk.nashorn.internal.parser.TokenType; 42 43/** 44 * UnaryNode nodes represent single operand operations. 45 */ 46@Immutable 47public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic { 48 private static final long serialVersionUID = 1L; 49 50 /** Right hand side argument. */ 51 private final Expression expression; 52 53 private final int programPoint; 54 55 private final Type type; 56 57 @Ignore 58 private static final List<TokenType> CAN_OVERFLOW = 59 Collections.unmodifiableList( 60 Arrays.asList(new TokenType[] { 61 TokenType.ADD, 62 TokenType.SUB, //negate 63 TokenType.DECPREFIX, 64 TokenType.DECPOSTFIX, 65 TokenType.INCPREFIX, 66 TokenType.INCPOSTFIX, 67 })); 68 69 /** 70 * Constructor 71 * 72 * @param token token 73 * @param rhs expression 74 */ 75 public UnaryNode(final long token, final Expression rhs) { 76 this(token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs); 77 } 78 79 /** 80 * Constructor 81 * 82 * @param token token 83 * @param start start 84 * @param finish finish 85 * @param expression expression 86 */ 87 public UnaryNode(final long token, final int start, final int finish, final Expression expression) { 88 super(token, start, finish); 89 this.expression = expression; 90 this.programPoint = INVALID_PROGRAM_POINT; 91 this.type = null; 92 } 93 94 95 private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint) { 96 super(unaryNode); 97 this.expression = expression; 98 this.programPoint = programPoint; 99 this.type = type; 100 } 101 102 /** 103 * Is this an assignment - i.e. that mutates something such as a++ 104 * 105 * @return true if assignment 106 */ 107 @Override 108 public boolean isAssignment() { 109 switch (tokenType()) { 110 case DECPOSTFIX: 111 case DECPREFIX: 112 case INCPOSTFIX: 113 case INCPREFIX: 114 return true; 115 default: 116 return false; 117 } 118 } 119 120 @Override 121 public boolean isSelfModifying() { 122 return isAssignment(); 123 } 124 125 @Override 126 public Type getWidestOperationType() { 127 switch (tokenType()) { 128 case ADD: 129 final Type operandType = getExpression().getType(); 130 if(operandType == Type.BOOLEAN) { 131 return Type.INT; 132 } else if(operandType.isObject()) { 133 return Type.NUMBER; 134 } 135 assert operandType.isNumeric(); 136 return operandType; 137 case SUB: 138 // This might seems overly conservative until you consider that -0 can only be represented as a double. 139 return Type.NUMBER; 140 case NOT: 141 case DELETE: 142 return Type.BOOLEAN; 143 case BIT_NOT: 144 return Type.INT; 145 case VOID: 146 return Type.UNDEFINED; 147 default: 148 return isAssignment() ? Type.NUMBER : Type.OBJECT; 149 } 150 } 151 152 @Override 153 public Expression getAssignmentDest() { 154 return isAssignment() ? getExpression() : null; 155 } 156 157 @Override 158 public UnaryNode setAssignmentDest(final Expression n) { 159 return setExpression(n); 160 } 161 162 @Override 163 public Expression getAssignmentSource() { 164 return getAssignmentDest(); 165 } 166 167 /** 168 * Assist in IR navigation. 169 * @param visitor IR navigating visitor. 170 */ 171 @Override 172 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 173 if (visitor.enterUnaryNode(this)) { 174 return visitor.leaveUnaryNode(setExpression((Expression)expression.accept(visitor))); 175 } 176 177 return this; 178 } 179 180 @Override 181 public boolean isLocal() { 182 switch (tokenType()) { 183 case NEW: 184 return false; 185 case ADD: 186 case SUB: 187 case NOT: 188 case BIT_NOT: 189 return expression.isLocal() && expression.getType().isJSPrimitive(); 190 case DECPOSTFIX: 191 case DECPREFIX: 192 case INCPOSTFIX: 193 case INCPREFIX: 194 return expression instanceof IdentNode && expression.isLocal() && expression.getType().isJSPrimitive(); 195 default: 196 return expression.isLocal(); 197 } 198 } 199 200 @Override 201 public void toString(final StringBuilder sb, final boolean printType) { 202 toString(sb, 203 new Runnable() { 204 @Override 205 public void run() { 206 getExpression().toString(sb, printType); 207 } 208 }, 209 printType); 210 } 211 212 /** 213 * Creates the string representation of this unary node, delegating the creation of the string representation of its 214 * operand to a specified runnable. 215 * @param sb the string builder to use 216 * @param rhsStringBuilder the runnable that appends the string representation of the operand to the string builder 217 * @param printType should we print type 218 * when invoked. 219 */ 220 public void toString(final StringBuilder sb, final Runnable rhsStringBuilder, final boolean printType) { 221 final TokenType tokenType = tokenType(); 222 final String name = tokenType.getName(); 223 final boolean isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX; 224 225 if (isOptimistic()) { 226 sb.append(Expression.OPT_IDENTIFIER); 227 } 228 boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false); 229 230 if (!isPostfix) { 231 if (name == null) { 232 sb.append(tokenType.name()); 233 rhsParen = true; 234 } else { 235 sb.append(name); 236 237 if (tokenType.ordinal() > BIT_NOT.ordinal()) { 238 sb.append(' '); 239 } 240 } 241 } 242 243 if (rhsParen) { 244 sb.append('('); 245 } 246 rhsStringBuilder.run(); 247 if (rhsParen) { 248 sb.append(')'); 249 } 250 251 if (isPostfix) { 252 sb.append(tokenType == DECPOSTFIX ? "--" : "++"); 253 } 254 } 255 256 /** 257 * Get the right hand side of this if it is inherited by a binary expression, 258 * or just the expression itself if still Unary 259 * 260 * @see BinaryNode 261 * 262 * @return right hand side or expression node 263 */ 264 public Expression getExpression() { 265 return expression; 266 } 267 268 /** 269 * Reset the right hand side of this if it is inherited by a binary expression, 270 * or just the expression itself if still Unary 271 * 272 * @see BinaryNode 273 * 274 * @param expression right hand side or expression node 275 * @return a node equivalent to this one except for the requested change. 276 */ 277 public UnaryNode setExpression(final Expression expression) { 278 if (this.expression == expression) { 279 return this; 280 } 281 return new UnaryNode(this, expression, type, programPoint); 282 } 283 284 @Override 285 public int getProgramPoint() { 286 return programPoint; 287 } 288 289 @Override 290 public UnaryNode setProgramPoint(final int programPoint) { 291 if (this.programPoint == programPoint) { 292 return this; 293 } 294 return new UnaryNode(this, expression, type, programPoint); 295 } 296 297 @Override 298 public boolean canBeOptimistic() { 299 return getMostOptimisticType() != getMostPessimisticType(); 300 } 301 302 @Override 303 public Type getMostOptimisticType() { 304 if (CAN_OVERFLOW.contains(tokenType())) { 305 return Type.INT; 306 } 307 return getMostPessimisticType(); 308 } 309 310 @Override 311 public Type getMostPessimisticType() { 312 return getWidestOperationType(); 313 } 314 315 @Override 316 public Type getType() { 317 final Type widest = getWidestOperationType(); 318 if(type == null) { 319 return widest; 320 } 321 return Type.narrowest(widest, Type.widest(type, expression.getType())); 322 } 323 324 @Override 325 public UnaryNode setType(final Type type) { 326 if (this.type == type) { 327 return this; 328 } 329 return new UnaryNode(this, expression, type, programPoint); 330 } 331 332} 333