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