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