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 }