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 static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
  29 import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
  30 import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
  31 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  32 
  33 import java.util.Arrays;
  34 import java.util.Collections;
  35 import java.util.List;
  36 import jdk.nashorn.internal.codegen.types.Type;
  37 import jdk.nashorn.internal.ir.annotations.Ignore;
  38 import jdk.nashorn.internal.ir.annotations.Immutable;
  39 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  40 import jdk.nashorn.internal.parser.Token;
  41 import jdk.nashorn.internal.parser.TokenType;
  42 
  43 /**
  44  * UnaryNode nodes represent single operand operations.
  45  */
  46 @Immutable
  47 public 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 }