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