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.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  29 
  30 import java.util.Arrays;
  31 import java.util.Collections;
  32 import java.util.HashSet;
  33 import java.util.Set;
  34 import jdk.nashorn.internal.codegen.types.Type;
  35 import jdk.nashorn.internal.ir.annotations.Ignore;
  36 import jdk.nashorn.internal.ir.annotations.Immutable;
  37 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  38 import jdk.nashorn.internal.parser.TokenType;
  39 
  40 /**
  41  * BinaryNode nodes represent two operand operations.
  42  */
  43 @Immutable
  44 public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
  45     private static final long serialVersionUID = 1L;
  46 
  47     // Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic
  48     // type calculation as it can have local variables as its operands that will decide its ultimate type.
  49     private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){/*empty*/}.getClass());
  50 
  51     /** Left hand side argument. */
  52     private final Expression lhs;
  53 
  54     private final Expression rhs;
  55 
  56     private final int programPoint;
  57 
  58     private final Type type;
  59     private transient Type cachedType;
  60 
  61     @Ignore
  62     private static final Set<TokenType> CAN_OVERFLOW =
  63         Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new TokenType[] {
  64                 TokenType.ADD,
  65                 TokenType.DIV,
  66                 TokenType.MOD,
  67                 TokenType.MUL,
  68                 TokenType.SUB,
  69                 TokenType.ASSIGN_ADD,
  70                 TokenType.ASSIGN_DIV,
  71                 TokenType.ASSIGN_MOD,
  72                 TokenType.ASSIGN_MUL,
  73                 TokenType.ASSIGN_SUB
  74             })));
  75 
  76     /**
  77      * Constructor
  78      *
  79      * @param token  token
  80      * @param lhs    left hand side
  81      * @param rhs    right hand side
  82      */
  83     public BinaryNode(final long token, final Expression lhs, final Expression rhs) {
  84         super(token, lhs.getStart(), rhs.getFinish());
  85         assert !(isTokenType(TokenType.AND) || isTokenType(TokenType.OR)) || lhs instanceof JoinPredecessorExpression;
  86         this.lhs   = lhs;
  87         this.rhs   = rhs;
  88         this.programPoint = INVALID_PROGRAM_POINT;
  89         this.type = null;
  90     }
  91 
  92     private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint) {
  93         super(binaryNode);
  94         this.lhs = lhs;
  95         this.rhs = rhs;
  96         this.programPoint = programPoint;
  97         this.type = type;
  98     }
  99 
 100     /**
 101      * Returns true if the node is a comparison operation (either equality, inequality, or relational).
 102      * @return true if the node is a comparison operation.
 103      */
 104     public boolean isComparison() {
 105         switch (tokenType()) {
 106         case EQ:
 107         case EQ_STRICT:
 108         case NE:
 109         case NE_STRICT:
 110         case LE:
 111         case LT:
 112         case GE:
 113         case GT:
 114             return true;
 115         default:
 116             return false;
 117         }
 118     }
 119 
 120     /**
 121      * Returns true if the node is a relational operation (less than (or equals), greater than (or equals)).
 122      * @return true if the node is a relational operation.
 123      */
 124     public boolean isRelational() {
 125         switch (tokenType()) {
 126         case LT:
 127         case GT:
 128         case LE:
 129         case GE:
 130             return true;
 131         default:
 132             return false;
 133         }
 134     }
 135 
 136     /**
 137      * Returns true if the node is a logical operation.
 138      * @return true if the node is a logical operation.
 139      */
 140     public boolean isLogical() {
 141         return isLogical(tokenType());
 142     }
 143 
 144     /**
 145      * Returns true if the token type represents a logical operation.
 146      * @param tokenType the token type
 147      * @return true if the token type represents a logical operation.
 148      */
 149     public static boolean isLogical(final TokenType tokenType) {
 150         switch (tokenType) {
 151         case AND:
 152         case OR:
 153             return true;
 154         default:
 155             return false;
 156         }
 157     }
 158 
 159     /**
 160      * Return the widest possible operand type for this operation.
 161      *
 162      * @return Type
 163      */
 164     public Type getWidestOperandType() {
 165         switch (tokenType()) {
 166         case SHR:
 167         case ASSIGN_SHR:
 168             return Type.INT;
 169         case INSTANCEOF:
 170             return Type.OBJECT;
 171         default:
 172             if (isComparison()) {
 173                 return Type.OBJECT;
 174             }
 175             return getWidestOperationType();
 176         }
 177     }
 178 
 179     @Override
 180     public Type getWidestOperationType() {
 181         switch (tokenType()) {
 182         case ADD:
 183         case ASSIGN_ADD: {
 184             // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type
 185             // calculation case while this handles the conservative case.
 186             final Type lhsType = lhs.getType();
 187             final Type rhsType = rhs.getType();
 188             if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
 189                 // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here,
 190                 // they'd end up being treated as generic INT operands and their sum would be conservatively considered
 191                 // to be a LONG in the generic case below; we can do better here.
 192                 return Type.INT;
 193             } else if(isString(lhsType) || isString(rhsType)) {
 194                 // We can statically figure out that this is a string if either operand is a string. In this case, use
 195                 // CHARSEQUENCE to prevent it from being proactively flattened.
 196                 return Type.CHARSEQUENCE;
 197             }
 198             final Type widestOperandType = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
 199             if(widestOperandType == Type.INT) {
 200                 return Type.LONG;
 201             } else if (widestOperandType.isNumeric()) {
 202                 return Type.NUMBER;
 203             }
 204             // We pretty much can't know what it will be statically. Must presume OBJECT conservatively, as we can end
 205             // up getting either a string or an object when adding something + object, e.g.:
 206             // 1 + {} == "1[object Object]", but
 207             // 1 + {valueOf: function() { return 2 }} == 3. Also:
 208             // 1 + {valueOf: function() { return "2" }} == "12".
 209             return Type.OBJECT;
 210         }
 211         case SHR:
 212         case ASSIGN_SHR:
 213             return Type.LONG;
 214         case ASSIGN_SAR:
 215         case ASSIGN_SHL:
 216         case BIT_AND:
 217         case BIT_OR:
 218         case BIT_XOR:
 219         case ASSIGN_BIT_AND:
 220         case ASSIGN_BIT_OR:
 221         case ASSIGN_BIT_XOR:
 222         case SAR:
 223         case SHL:
 224             return Type.INT;
 225         case DIV:
 226         case MOD:
 227         case ASSIGN_DIV:
 228         case ASSIGN_MOD: {
 229             // Naively, one might think MOD has the same type as the widest of its operands, this is unfortunately not
 230             // true when denominator is zero, so even type(int % int) == double.
 231             return Type.NUMBER;
 232         }
 233         case MUL:
 234         case SUB:
 235         case ASSIGN_MUL:
 236         case ASSIGN_SUB: {
 237             final Type lhsType = lhs.getType();
 238             final Type rhsType = rhs.getType();
 239             if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
 240                 return Type.INT;
 241             }
 242             final Type widestOperandType = Type.widest(booleanToInt(lhsType), booleanToInt(rhsType));
 243             if(widestOperandType == Type.INT) {
 244                 return Type.LONG;
 245             }
 246             return Type.NUMBER;
 247         }
 248         case VOID: {
 249             return Type.UNDEFINED;
 250         }
 251         case ASSIGN: {
 252             return rhs.getType();
 253         }
 254         case INSTANCEOF: {
 255             return Type.BOOLEAN;
 256         }
 257         case COMMALEFT: {
 258             return lhs.getType();
 259         }
 260         case COMMARIGHT: {
 261             return rhs.getType();
 262         }
 263         case AND:
 264         case OR:{
 265             return Type.widestReturnType(lhs.getType(), rhs.getType());
 266         }
 267         default:
 268             if (isComparison()) {
 269                 return Type.BOOLEAN;
 270             }
 271             return Type.OBJECT;
 272         }
 273     }
 274 
 275     private static boolean isString(final Type type) {
 276         return type == Type.STRING || type == Type.CHARSEQUENCE;
 277     }
 278 
 279     private static Type booleanToInt(final Type type) {
 280         return type == Type.BOOLEAN ? Type.INT : type;
 281     }
 282 
 283     private static Type undefinedToNumber(final Type type) {
 284         return type == Type.UNDEFINED ? Type.NUMBER : type;
 285     }
 286 
 287     /**
 288      * Check if this node is an assignment
 289      *
 290      * @return true if this node assigns a value
 291      */
 292     @Override
 293     public boolean isAssignment() {
 294         switch (tokenType()) {
 295         case ASSIGN:
 296         case ASSIGN_ADD:
 297         case ASSIGN_BIT_AND:
 298         case ASSIGN_BIT_OR:
 299         case ASSIGN_BIT_XOR:
 300         case ASSIGN_DIV:
 301         case ASSIGN_MOD:
 302         case ASSIGN_MUL:
 303         case ASSIGN_SAR:
 304         case ASSIGN_SHL:
 305         case ASSIGN_SHR:
 306         case ASSIGN_SUB:
 307            return true;
 308         default:
 309            return false;
 310         }
 311     }
 312 
 313     @Override
 314     public boolean isSelfModifying() {
 315         return isAssignment() && !isTokenType(TokenType.ASSIGN);
 316     }
 317 
 318     @Override
 319     public Expression getAssignmentDest() {
 320         return isAssignment() ? lhs() : null;
 321     }
 322 
 323     @Override
 324     public BinaryNode setAssignmentDest(final Expression n) {
 325         return setLHS(n);
 326     }
 327 
 328     @Override
 329     public Expression getAssignmentSource() {
 330         return rhs();
 331     }
 332 
 333     /**
 334      * Assist in IR navigation.
 335      * @param visitor IR navigating visitor.
 336      */
 337     @Override
 338     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 339         if (visitor.enterBinaryNode(this)) {
 340             return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
 341         }
 342 
 343         return this;
 344     }
 345 
 346     @Override
 347     public boolean isLocal() {
 348         switch (tokenType()) {
 349         case SAR:
 350         case SHL:
 351         case SHR:
 352         case BIT_AND:
 353         case BIT_OR:
 354         case BIT_XOR:
 355         case ADD:
 356         case DIV:
 357         case MOD:
 358         case MUL:
 359         case SUB:
 360             return lhs.isLocal() && lhs.getType().isJSPrimitive()
 361                 && rhs.isLocal() && rhs.getType().isJSPrimitive();
 362         case ASSIGN_ADD:
 363         case ASSIGN_BIT_AND:
 364         case ASSIGN_BIT_OR:
 365         case ASSIGN_BIT_XOR:
 366         case ASSIGN_DIV:
 367         case ASSIGN_MOD:
 368         case ASSIGN_MUL:
 369         case ASSIGN_SAR:
 370         case ASSIGN_SHL:
 371         case ASSIGN_SHR:
 372         case ASSIGN_SUB:
 373             return lhs instanceof IdentNode && lhs.isLocal() && lhs.getType().isJSPrimitive()
 374                     && rhs.isLocal() && rhs.getType().isJSPrimitive();
 375         case ASSIGN:
 376             return lhs instanceof IdentNode && lhs.isLocal() && rhs.isLocal();
 377         default:
 378             return false;
 379         }
 380     }
 381 
 382     @Override
 383     public boolean isAlwaysFalse() {
 384         switch (tokenType()) {
 385         case COMMALEFT:
 386             return lhs.isAlwaysFalse();
 387         case COMMARIGHT:
 388             return rhs.isAlwaysFalse();
 389         default:
 390             return false;
 391         }
 392     }
 393 
 394     @Override
 395     public boolean isAlwaysTrue() {
 396         switch (tokenType()) {
 397         case COMMALEFT:
 398             return lhs.isAlwaysTrue();
 399         case COMMARIGHT:
 400             return rhs.isAlwaysTrue();
 401         default:
 402             return false;
 403         }
 404     }
 405 
 406     @Override
 407     public void toString(final StringBuilder sb, final boolean printType) {
 408         final TokenType tokenType = tokenType();
 409 
 410         final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
 411         final boolean rhsParen = tokenType.needsParens(rhs().tokenType(), false);
 412 
 413         if (lhsParen) {
 414             sb.append('(');
 415         }
 416 
 417         lhs().toString(sb, printType);
 418 
 419         if (lhsParen) {
 420             sb.append(')');
 421         }
 422 
 423         sb.append(' ');
 424 
 425         switch (tokenType) {
 426         case COMMALEFT:
 427             sb.append(",<");
 428             break;
 429         case COMMARIGHT:
 430             sb.append(",>");
 431             break;
 432         case INCPREFIX:
 433         case DECPREFIX:
 434             sb.append("++");
 435             break;
 436         default:
 437             sb.append(tokenType.getName());
 438             break;
 439         }
 440 
 441         if (isOptimistic()) {
 442             sb.append(Expression.OPT_IDENTIFIER);
 443         }
 444 
 445         sb.append(' ');
 446 
 447         if (rhsParen) {
 448             sb.append('(');
 449         }
 450         rhs().toString(sb, printType);
 451         if (rhsParen) {
 452             sb.append(')');
 453         }
 454     }
 455 
 456     /**
 457      * Get the left hand side expression for this node
 458      * @return the left hand side expression
 459      */
 460     public Expression lhs() {
 461         return lhs;
 462     }
 463 
 464     /**
 465      * Get the right hand side expression for this node
 466      * @return the left hand side expression
 467      */
 468     public Expression rhs() {
 469         return rhs;
 470     }
 471 
 472     /**
 473      * Set the left hand side expression for this node
 474      * @param lhs new left hand side expression
 475      * @return a node equivalent to this one except for the requested change.
 476      */
 477     public BinaryNode setLHS(final Expression lhs) {
 478         if (this.lhs == lhs) {
 479             return this;
 480         }
 481         return new BinaryNode(this, lhs, rhs, type, programPoint);
 482     }
 483 
 484     /**
 485      * Set the right hand side expression for this node
 486      * @param rhs new right hand side expression
 487      * @return a node equivalent to this one except for the requested change.
 488      */
 489     public BinaryNode setRHS(final Expression rhs) {
 490         if (this.rhs == rhs) {
 491             return this;
 492         }
 493         return new BinaryNode(this, lhs, rhs, type, programPoint);
 494     }
 495 
 496     /**
 497      * Set both the left and the right hand side expression for this node
 498      * @param lhs new left hand side expression
 499      * @param rhs new left hand side expression
 500      * @return a node equivalent to this one except for the requested change.
 501      */
 502     public BinaryNode setOperands(final Expression lhs, final Expression rhs) {
 503         if (this.lhs == lhs && this.rhs == rhs) {
 504             return this;
 505         }
 506         return new BinaryNode(this, lhs, rhs, type, programPoint);
 507     }
 508 
 509     @Override
 510     public int getProgramPoint() {
 511         return programPoint;
 512     }
 513 
 514     @Override
 515     public boolean canBeOptimistic() {
 516         return isTokenType(TokenType.ADD) || (getMostOptimisticType() != getMostPessimisticType());
 517     }
 518 
 519     @Override
 520     public BinaryNode setProgramPoint(final int programPoint) {
 521         if (this.programPoint == programPoint) {
 522             return this;
 523         }
 524         return new BinaryNode(this, lhs, rhs, type, programPoint);
 525     }
 526 
 527     @Override
 528     public Type getMostOptimisticType() {
 529         final TokenType tokenType = tokenType();
 530         if(tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
 531             return OPTIMISTIC_UNDECIDED_TYPE;
 532         } else if (CAN_OVERFLOW.contains(tokenType)) {
 533             return Type.INT;
 534         }
 535         return getMostPessimisticType();
 536     }
 537 
 538     @Override
 539     public Type getMostPessimisticType() {
 540         return getWidestOperationType();
 541     }
 542 
 543     /**
 544      * Returns true if the node has the optimistic type of the node is not yet decided. Optimistic ADD nodes start out
 545      * as undecided until we can figure out if they're numeric or not.
 546      * @return true if the node has the optimistic type of the node is not yet decided.
 547      */
 548     public boolean isOptimisticUndecidedType() {
 549         return type == OPTIMISTIC_UNDECIDED_TYPE;
 550     }
 551 
 552     @Override
 553     public Type getType() {
 554         if (cachedType == null) {
 555             cachedType = getTypeUncached();
 556         }
 557         return cachedType;
 558     }
 559 
 560     private Type getTypeUncached() {
 561         if(type == OPTIMISTIC_UNDECIDED_TYPE) {
 562             return decideType(lhs.getType(), rhs.getType());
 563         }
 564         final Type widest = getWidestOperationType();
 565         if(type == null) {
 566             return widest;
 567         }
 568         return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(), rhs.getType())));
 569     }
 570 
 571     private static Type decideType(final Type lhsType, final Type rhsType) {
 572         // Compare this to getWidestOperationType() for ADD and ASSIGN_ADD cases. There's some similar logic, but these
 573         // are optimistic decisions, meaning that we don't have to treat boolean addition separately (as it'll become
 574         // int addition in the general case anyway), and that we also don't conservatively widen sums of ints to
 575         // longs, or sums of longs to doubles.
 576         if(isString(lhsType) || isString(rhsType)) {
 577             return Type.CHARSEQUENCE;
 578         }
 579         // NOTE: We don't have optimistic object-to-(int, long) conversions. Therefore, if any operand is an Object, we
 580         // bail out of optimism here and presume a conservative Object return value, as the object's ToPrimitive() can
 581         // end up returning either a number or a string, and their common supertype is Object, for better or worse.
 582         final Type widest = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
 583         return widest.isObject() ? Type.OBJECT : widest;
 584     }
 585 
 586     /**
 587      * If the node is a node representing an add operation and has {@link #isOptimisticUndecidedType() optimistic
 588      * undecided type}, decides its type. Should be invoked after its operands types have been finalized.
 589      * @return returns a new node similar to this node, but with its type set to the type decided from the type of its
 590      * operands.
 591      */
 592     public BinaryNode decideType() {
 593         assert type == OPTIMISTIC_UNDECIDED_TYPE;
 594         return setType(decideType(lhs.getType(), rhs.getType()));
 595     }
 596 
 597     @Override
 598     public BinaryNode setType(final Type type) {
 599         if (this.type == type) {
 600             return this;
 601         }
 602         return new BinaryNode(this, lhs, rhs, type, programPoint);
 603     }
 604 }