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