1 /*
   2  * Copyright (c) 2010, 2015, 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 java.util.Arrays;
  29 import java.util.Collections;
  30 import java.util.List;
  31 import jdk.nashorn.internal.codegen.types.Type;
  32 import jdk.nashorn.internal.ir.annotations.Immutable;
  33 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  34 import jdk.nashorn.internal.parser.TokenType;
  35 
  36 /**
  37  * IR representation for a runtime call.
  38  */
  39 @Immutable
  40 public class RuntimeNode extends Expression {
  41     private static final long serialVersionUID = 1L;
  42 
  43     /**
  44      * Request enum used for meta-information about the runtime request
  45      */
  46     public enum Request {
  47         /** An addition with at least one object */
  48         ADD(TokenType.ADD, Type.OBJECT, 2, true),
  49         /** Request to enter debugger */
  50         DEBUGGER,
  51         /** New operator */
  52         NEW,
  53         /** Typeof operator */
  54         TYPEOF,
  55         /** Reference error type */
  56         REFERENCE_ERROR,
  57         /** === operator with at least one object */
  58         EQ_STRICT(TokenType.EQ_STRICT, Type.BOOLEAN, 2, true),
  59         /** == operator with at least one object */
  60         EQ(TokenType.EQ, Type.BOOLEAN, 2, true),
  61         /** {@literal >=} operator with at least one object */
  62         GE(TokenType.GE, Type.BOOLEAN, 2, true),
  63         /** {@literal >} operator with at least one object */
  64         GT(TokenType.GT, Type.BOOLEAN, 2, true),
  65         /** in operator */
  66         IN(TokenType.IN, Type.BOOLEAN, 2),
  67         /** instanceof operator */
  68         INSTANCEOF(TokenType.INSTANCEOF, Type.BOOLEAN, 2),
  69         /** {@literal <=} operator with at least one object */
  70         LE(TokenType.LE, Type.BOOLEAN, 2, true),
  71         /** {@literal <} operator with at least one object */
  72         LT(TokenType.LT, Type.BOOLEAN, 2, true),
  73         /** !== operator with at least one object */
  74         NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
  75         /** != operator with at least one object */
  76         NE(TokenType.NE, Type.BOOLEAN, 2, true),
  77         /** is undefined */
  78         IS_UNDEFINED(TokenType.EQ_STRICT, Type.BOOLEAN, 2),
  79         /** is not undefined */
  80         IS_NOT_UNDEFINED(TokenType.NE_STRICT, Type.BOOLEAN, 2),
  81         /** Get template object from raw and cooked string arrays. */
  82         GET_TEMPLATE_OBJECT(TokenType.TEMPLATE, Type.SCRIPT_OBJECT, 2);
  83 
  84         /** token type */
  85         private final TokenType tokenType;
  86 
  87         /** return type for request */
  88         private final Type returnType;
  89 
  90         /** arity of request */
  91         private final int arity;
  92 
  93         /** Can the specializer turn this into something that works with 1 or more primitives? */
  94         private final boolean canSpecialize;
  95 
  96         private Request() {
  97             this(TokenType.VOID, Type.OBJECT, 0);
  98         }
  99 
 100         private Request(final TokenType tokenType, final Type returnType, final int arity) {
 101             this(tokenType, returnType, arity, false);
 102         }
 103 
 104         private Request(final TokenType tokenType, final Type returnType, final int arity, final boolean canSpecialize) {
 105             this.tokenType     = tokenType;
 106             this.returnType    = returnType;
 107             this.arity         = arity;
 108             this.canSpecialize = canSpecialize;
 109         }
 110 
 111         /**
 112          * Can this request type be specialized?
 113          *
 114          * @return true if request can be specialized
 115          */
 116         public boolean canSpecialize() {
 117             return canSpecialize;
 118         }
 119 
 120         /**
 121          * Get arity
 122          *
 123          * @return the arity of the request
 124          */
 125         public int getArity() {
 126             return arity;
 127         }
 128 
 129         /**
 130          * Get the return type
 131          *
 132          * @return return type for request
 133          */
 134         public Type getReturnType() {
 135             return returnType;
 136         }
 137 
 138         /**
 139          * Get token type
 140          *
 141          * @return token type for request
 142          */
 143         public TokenType getTokenType() {
 144             return tokenType;
 145         }
 146 
 147         /**
 148          * Get the non-strict name for this request.
 149          *
 150          * @return the name without _STRICT suffix
 151          */
 152         public String nonStrictName() {
 153             switch(this) {
 154             case NE_STRICT:
 155                 return NE.name();
 156             case EQ_STRICT:
 157                 return EQ.name();
 158             default:
 159                 return name();
 160             }
 161         }
 162 
 163         /**
 164          * Derive a runtime node request type for a node
 165          * @param node the node
 166          * @return request type
 167          */
 168         public static Request requestFor(final Expression node) {
 169             switch (node.tokenType()) {
 170             case TYPEOF:
 171                 return Request.TYPEOF;
 172             case IN:
 173                 return Request.IN;
 174             case INSTANCEOF:
 175                 return Request.INSTANCEOF;
 176             case EQ_STRICT:
 177                 return Request.EQ_STRICT;
 178             case NE_STRICT:
 179                 return Request.NE_STRICT;
 180             case EQ:
 181                 return Request.EQ;
 182             case NE:
 183                 return Request.NE;
 184             case LT:
 185                 return Request.LT;
 186             case LE:
 187                 return Request.LE;
 188             case GT:
 189                 return Request.GT;
 190             case GE:
 191                 return Request.GE;
 192             default:
 193                 assert false;
 194                 return null;
 195             }
 196         }
 197 
 198         /**
 199          * Is this an undefined check?
 200          *
 201          * @param request request
 202          *
 203          * @return true if undefined check
 204          */
 205         public static boolean isUndefinedCheck(final Request request) {
 206             return request == IS_UNDEFINED || request == IS_NOT_UNDEFINED;
 207         }
 208 
 209         /**
 210          * Is this an EQ or EQ_STRICT?
 211          *
 212          * @param request a request
 213          *
 214          * @return true if EQ or EQ_STRICT
 215          */
 216         public static boolean isEQ(final Request request) {
 217             return request == EQ || request == EQ_STRICT;
 218         }
 219 
 220         /**
 221          * Is this an NE or NE_STRICT?
 222          *
 223          * @param request a request
 224          *
 225          * @return true if NE or NE_STRICT
 226          */
 227         public static boolean isNE(final Request request) {
 228             return request == NE || request == NE_STRICT;
 229         }
 230 
 231         /**
 232          * Is this strict?
 233          *
 234          * @param request a request
 235          *
 236          * @return true if script
 237          */
 238         public static boolean isStrict(final Request request) {
 239             return request == EQ_STRICT || request == NE_STRICT;
 240         }
 241 
 242         /**
 243          * If this request can be reversed, return the reverse request
 244          * Eq EQ {@literal ->} NE.
 245          *
 246          * @param request request to reverse
 247          *
 248          * @return reversed request or null if not applicable
 249          */
 250         public static Request reverse(final Request request) {
 251             switch (request) {
 252             case EQ:
 253             case EQ_STRICT:
 254             case NE:
 255             case NE_STRICT:
 256                 return request;
 257             case LE:
 258                 return GE;
 259             case LT:
 260                 return GT;
 261             case GE:
 262                 return LE;
 263             case GT:
 264                 return LT;
 265             default:
 266                 return null;
 267             }
 268         }
 269 
 270         /**
 271          * Invert the request, only for non equals comparisons.
 272          *
 273          * @param request a request
 274          *
 275          * @return the inverted request, or null if not applicable
 276          */
 277         public static Request invert(final Request request) {
 278             switch (request) {
 279             case EQ:
 280                 return NE;
 281             case EQ_STRICT:
 282                 return NE_STRICT;
 283             case NE:
 284                 return EQ;
 285             case NE_STRICT:
 286                 return EQ_STRICT;
 287             case LE:
 288                 return GT;
 289             case LT:
 290                 return GE;
 291             case GE:
 292                 return LT;
 293             case GT:
 294                 return LE;
 295             default:
 296                 return null;
 297             }
 298         }
 299 
 300         /**
 301          * Check if this is a comparison
 302          *
 303          * @param request a request
 304          *
 305          * @return true if this is a comparison, null otherwise
 306          */
 307         public static boolean isComparison(final Request request) {
 308             switch (request) {
 309             case EQ:
 310             case EQ_STRICT:
 311             case NE:
 312             case NE_STRICT:
 313             case LE:
 314             case LT:
 315             case GE:
 316             case GT:
 317             case IS_UNDEFINED:
 318             case IS_NOT_UNDEFINED:
 319                 return true;
 320             default:
 321                 return false;
 322             }
 323         }
 324     }
 325 
 326     /** Runtime request. */
 327     private final Request request;
 328 
 329     /** Call arguments. */
 330     private final List<Expression> args;
 331 
 332     /**
 333      * Constructor
 334      *
 335      * @param token   token
 336      * @param finish  finish
 337      * @param request the request
 338      * @param args    arguments to request
 339      */
 340     public RuntimeNode(final long token, final int finish, final Request request, final List<Expression> args) {
 341         super(token, finish);
 342 
 343         this.request      = request;
 344         this.args         = args;
 345     }
 346 
 347     private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final List<Expression> args) {
 348         super(runtimeNode);
 349 
 350         this.request      = request;
 351         this.args         = args;
 352     }
 353 
 354     /**
 355      * Constructor
 356      *
 357      * @param token   token
 358      * @param finish  finish
 359      * @param request the request
 360      * @param args    arguments to request
 361      */
 362     public RuntimeNode(final long token, final int finish, final Request request, final Expression... args) {
 363         this(token, finish, request, Arrays.asList(args));
 364     }
 365 
 366     /**
 367      * Constructor
 368      *
 369      * @param parent  parent node from which to inherit source, token, finish
 370      * @param request the request
 371      * @param args    arguments to request
 372      */
 373     public RuntimeNode(final Expression parent, final Request request, final Expression... args) {
 374         this(parent, request, Arrays.asList(args));
 375     }
 376 
 377     /**
 378      * Constructor
 379      *
 380      * @param parent  parent node from which to inherit source, token, finish
 381      * @param request the request
 382      * @param args    arguments to request
 383      */
 384     public RuntimeNode(final Expression parent, final Request request, final List<Expression> args) {
 385         super(parent);
 386 
 387         this.request      = request;
 388         this.args         = args;
 389     }
 390 
 391     /**
 392      * Constructor
 393      *
 394      * @param parent  parent node from which to inherit source, token, finish and arguments
 395      * @param request the request
 396      */
 397     public RuntimeNode(final UnaryNode parent, final Request request) {
 398         this(parent, request, parent.getExpression());
 399     }
 400 
 401     /**
 402      * Constructor used to replace a binary node with a runtime request.
 403      *
 404      * @param parent  parent node from which to inherit source, token, finish and arguments
 405      */
 406     public RuntimeNode(final BinaryNode parent) {
 407         this(parent, Request.requestFor(parent), parent.lhs(), parent.rhs());
 408     }
 409 
 410     /**
 411      * Reset the request for this runtime node
 412      * @param request request
 413      * @return new runtime node or same if same request
 414      */
 415     public RuntimeNode setRequest(final Request request) {
 416         if (this.request == request) {
 417             return this;
 418         }
 419         return new RuntimeNode(this, request, args);
 420    }
 421 
 422     /**
 423      * Return type for the ReferenceNode
 424      */
 425     @Override
 426     public Type getType() {
 427         return request.getReturnType();
 428     }
 429 
 430     @Override
 431     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 432         if (visitor.enterRuntimeNode(this)) {
 433             return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args)));
 434         }
 435 
 436         return this;
 437     }
 438 
 439     @Override
 440     public void toString(final StringBuilder sb, final boolean printType) {
 441         sb.append("ScriptRuntime.");
 442         sb.append(request);
 443         sb.append('(');
 444 
 445         boolean first = true;
 446 
 447         for (final Node arg : args) {
 448             if (!first) {
 449                 sb.append(", ");
 450             } else {
 451                 first = false;
 452             }
 453 
 454             arg.toString(sb, printType);
 455         }
 456 
 457         sb.append(')');
 458     }
 459 
 460     /**
 461      * Get the arguments for this runtime node
 462      * @return argument list
 463      */
 464     public List<Expression> getArgs() {
 465         return Collections.unmodifiableList(args);
 466     }
 467 
 468     /**
 469      * Set the arguments of this runtime node
 470      * @param args new arguments
 471      * @return new runtime node, or identical if no change
 472      */
 473     public RuntimeNode setArgs(final List<Expression> args) {
 474         if (this.args == args) {
 475             return this;
 476         }
 477         return new RuntimeNode(this, request, args);
 478     }
 479 
 480     /**
 481      * Get the request that this runtime node implements
 482      * @return the request
 483      */
 484     public Request getRequest() {
 485         return request;
 486     }
 487 
 488     /**
 489      * Is this runtime node, engineered to handle the "at least one object" case of the defined
 490      * requests and specialize on demand, really primitive. This can happen e.g. after AccessSpecializer
 491      * In that case it can be turned into a simpler primitive form in CodeGenerator
 492      *
 493      * @return true if all arguments now are primitive
 494      */
 495     public boolean isPrimitive() {
 496         for (final Expression arg : args) {
 497             if (arg.getType().isObject()) {
 498                 return false;
 499             }
 500         }
 501         return true;
 502     }
 503 }