CallNode.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.io.Serializable; 31import java.util.Collections; 32import java.util.List; 33import jdk.nashorn.internal.codegen.types.Type; 34import jdk.nashorn.internal.ir.annotations.Ignore; 35import jdk.nashorn.internal.ir.annotations.Immutable; 36import jdk.nashorn.internal.ir.visitor.NodeVisitor; 37 38/** 39 * IR representation for a function call. 40 */ 41@Immutable 42public final class CallNode extends LexicalContextExpression implements Optimistic { 43 private static final long serialVersionUID = 1L; 44 45 /** Function identifier or function body. */ 46 private final Expression function; 47 48 /** Call arguments. */ 49 private final List<Expression> args; 50 51 /** Is this a "new" operation */ 52 private static final int IS_NEW = 1 << 0; 53 54 /** Can this be a Function.call? */ 55 private static final int IS_APPLY_TO_CALL = 1 << 1; 56 57 private final int flags; 58 59 private final int lineNumber; 60 61 private final int programPoint; 62 63 private final Type optimisticType; 64 65 /** 66 * Arguments to be passed to builtin {@code eval} function 67 */ 68 public static class EvalArgs implements Serializable { 69 private static final long serialVersionUID = 1L; 70 private final List<Expression> args; 71 72 /** location string for the eval call */ 73 private final String location; 74 75 /** 76 * Constructor 77 * 78 * @param args arguments to eval 79 * @param location location for the eval call 80 */ 81 public EvalArgs(final List<Expression> args, final String location) { 82 this.args = args; 83 this.location = location; 84 } 85 86 /** 87 * Return the code that is to be eval:ed by this eval function 88 * @return code as an AST node 89 */ 90 public List<Expression> getArgs() { 91 return Collections.unmodifiableList(args); 92 } 93 94 private EvalArgs setArgs(final List<Expression> args) { 95 if (this.args == args) { 96 return this; 97 } 98 return new EvalArgs(args, location); 99 } 100 101 /** 102 * Get the human readable location for this eval call 103 * @return the location 104 */ 105 public String getLocation() { 106 return this.location; 107 } 108 } 109 110 /** arguments for 'eval' call. Non-null only if this call node is 'eval' */ 111 @Ignore 112 private final EvalArgs evalArgs; 113 114 /** 115 * Constructors 116 * 117 * @param lineNumber line number 118 * @param token token 119 * @param finish finish 120 * @param function the function to call 121 * @param args args to the call 122 * @param isNew true if this is a constructor call with the "new" keyword 123 */ 124 public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args, final boolean isNew) { 125 super(token, finish); 126 127 this.function = function; 128 this.args = args; 129 this.flags = isNew ? IS_NEW : 0; 130 this.evalArgs = null; 131 this.lineNumber = lineNumber; 132 this.programPoint = INVALID_PROGRAM_POINT; 133 this.optimisticType = null; 134 } 135 136 private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) { 137 super(callNode); 138 this.lineNumber = callNode.lineNumber; 139 this.function = function; 140 this.args = args; 141 this.flags = flags; 142 this.evalArgs = evalArgs; 143 this.programPoint = programPoint; 144 this.optimisticType = optimisticType; 145 } 146 147 /** 148 * Returns the line number. 149 * @return the line number. 150 */ 151 public int getLineNumber() { 152 return lineNumber; 153 } 154 155 @Override 156 public Type getType() { 157 return optimisticType == null ? Type.OBJECT : optimisticType; 158 } 159 160 @Override 161 public Optimistic setType(final Type optimisticType) { 162 if (this.optimisticType == optimisticType) { 163 return this; 164 } 165 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 166 } 167 168 /** 169 * Assist in IR navigation. 170 * 171 * @param visitor IR navigating visitor. 172 * 173 * @return node or replacement 174 */ 175 @Override 176 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 177 if (visitor.enterCallNode(this)) { 178 final CallNode newCallNode = (CallNode)visitor.leaveCallNode( 179 setFunction((Expression)function.accept(visitor)). 180 setArgs(Node.accept(visitor, args)). 181 setEvalArgs(evalArgs == null ? 182 null : 183 evalArgs.setArgs(Node.accept(visitor, evalArgs.getArgs())))); 184 // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice, 185 // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now. 186 if (this != newCallNode) { 187 return Node.replaceInLexicalContext(lc, this, newCallNode); 188 } 189 } 190 191 return this; 192 } 193 194 @Override 195 public void toString(final StringBuilder sb, final boolean printType) { 196 if (printType) { 197 optimisticTypeToString(sb); 198 } 199 200 final StringBuilder fsb = new StringBuilder(); 201 function.toString(fsb, printType); 202 203 if (isApplyToCall()) { 204 sb.append(fsb.toString().replace("apply", "[apply => call]")); 205 } else { 206 sb.append(fsb); 207 } 208 209 sb.append('('); 210 211 boolean first = true; 212 213 for (final Node arg : args) { 214 if (!first) { 215 sb.append(", "); 216 } else { 217 first = false; 218 } 219 220 arg.toString(sb, printType); 221 } 222 223 sb.append(')'); 224 } 225 226 /** 227 * Get the arguments for the call 228 * @return a list of arguments 229 */ 230 public List<Expression> getArgs() { 231 return Collections.unmodifiableList(args); 232 } 233 234 /** 235 * Reset the arguments for the call 236 * @param args new arguments list 237 * @return new callnode, or same if unchanged 238 */ 239 public CallNode setArgs(final List<Expression> args) { 240 if (this.args == args) { 241 return this; 242 } 243 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 244 } 245 246 /** 247 * If this call is an {@code eval} call, get its EvalArgs structure 248 * @return EvalArgs for call 249 */ 250 public EvalArgs getEvalArgs() { 251 return evalArgs; 252 } 253 254 /** 255 * Set the EvalArgs structure for this call, if it has been determined it is an 256 * {@code eval} 257 * 258 * @param evalArgs eval args 259 * @return same node or new one on state change 260 */ 261 public CallNode setEvalArgs(final EvalArgs evalArgs) { 262 if (this.evalArgs == evalArgs) { 263 return this; 264 } 265 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 266 } 267 268 /** 269 * Check if this call is a call to {@code eval} 270 * @return true if this is a call to {@code eval} 271 */ 272 public boolean isEval() { 273 return evalArgs != null; 274 } 275 276 /** 277 * Is this an apply call that we optimistically should try to turn into 278 * a call instead 279 * @return true if apply to call 280 */ 281 public boolean isApplyToCall() { 282 return (flags & IS_APPLY_TO_CALL) != 0; 283 } 284 285 /** 286 * Flag this call node as one that tries to call call instead of apply 287 * @return new call node with changed flags, if not already flagged as apply to call, then the same node 288 */ 289 public CallNode setIsApplyToCall() { 290 return setFlags(flags | IS_APPLY_TO_CALL); 291 } 292 293 /** 294 * Return the function expression that this call invokes 295 * @return the function 296 */ 297 public Expression getFunction() { 298 return function; 299 } 300 301 /** 302 * Reset the function expression that this call invokes 303 * @param function the function 304 * @return same node or new one on state change 305 */ 306 public CallNode setFunction(final Expression function) { 307 if (this.function == function) { 308 return this; 309 } 310 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 311 } 312 313 /** 314 * Check if this call is a new operation 315 * @return true if this a new operation 316 */ 317 public boolean isNew() { 318 return (flags & IS_NEW) != 0; 319 } 320 321 private CallNode setFlags(final int flags) { 322 if (this.flags == flags) { 323 return this; 324 } 325 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 326 } 327 328 @Override 329 public int getProgramPoint() { 330 return programPoint; 331 } 332 333 @Override 334 public CallNode setProgramPoint(final int programPoint) { 335 if (this.programPoint == programPoint) { 336 return this; 337 } 338 return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint); 339 } 340 341 @Override 342 public Type getMostOptimisticType() { 343 return Type.INT; 344 } 345 346 @Override 347 public Type getMostPessimisticType() { 348 return Type.OBJECT; 349 } 350 351 @Override 352 public boolean canBeOptimistic() { 353 return true; 354 } 355} 356