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 26 package jdk.nashorn.internal.ir; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__; 31 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 32 33 import jdk.nashorn.internal.codegen.types.Type; 34 import jdk.nashorn.internal.ir.annotations.Immutable; 35 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 36 import jdk.nashorn.internal.parser.Token; 37 import jdk.nashorn.internal.parser.TokenType; 38 39 /** 40 * IR representation for an identifier. 41 */ 42 @Immutable 43 public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic, JoinPredecessor { 44 private static final long serialVersionUID = 1L; 45 46 private static final int PROPERTY_NAME = 1 << 0; 47 private static final int INITIALIZED_HERE = 1 << 1; 48 private static final int FUNCTION = 1 << 2; 49 private static final int FUTURESTRICT_NAME = 1 << 3; 50 private static final int IS_DECLARED_HERE = 1 << 4; 51 private static final int IS_DEAD = 1 << 5; 52 private static final int DIRECT_SUPER = 1 << 6; 53 private static final int REST_PARAMETER = 1 << 7; 54 private static final int PROTO_PROPERTY = 1 << 8; 55 private static final int DEFAULT_PARAMETER = 1 << 9; 56 private static final int DESTRUCTURED_PARAMETER = 1 << 10; 57 58 /** Identifier. */ 59 private final String name; 60 61 /** Optimistic type */ 62 private final Type type; 63 64 private final int flags; 65 66 private final int programPoint; 67 68 private final LocalVariableConversion conversion; 69 70 private Symbol symbol; 71 72 73 /** 74 * Constructor 75 * 76 * @param token token 77 * @param finish finish position 78 * @param name name of identifier 79 */ 80 public IdentNode(final long token, final int finish, final String name) { 81 super(token, finish); 82 this.name = name; 83 this.type = null; 84 this.flags = 0; 85 this.programPoint = INVALID_PROGRAM_POINT; 86 this.conversion = null; 87 } 88 89 private IdentNode(final IdentNode identNode, final String name, final Type type, final int flags, final int programPoint, final LocalVariableConversion conversion) { 90 super(identNode); 91 this.name = name; 92 this.type = type; 93 this.flags = flags; 94 this.programPoint = programPoint; 95 this.conversion = conversion; 96 this.symbol = identNode.symbol; 97 } 98 99 /** 100 * Copy constructor - create a new IdentNode for the same location 101 * 102 * @param identNode identNode 103 */ 104 public IdentNode(final IdentNode identNode) { 105 super(identNode); 106 this.name = identNode.getName(); 107 this.type = identNode.type; 108 this.flags = identNode.flags; 109 this.conversion = identNode.conversion; 110 this.programPoint = INVALID_PROGRAM_POINT; 111 this.symbol = identNode.symbol; 112 } 113 114 /** 115 * Creates an identifier for the symbol. Normally used by code generator for creating temporary storage identifiers 116 * that must contain both a symbol and a type. 117 * @param symbol the symbol to create a temporary identifier for. 118 * @return a temporary identifier for the symbol. 119 */ 120 public static IdentNode createInternalIdentifier(final Symbol symbol) { 121 return new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol); 122 } 123 124 @Override 125 public Type getType() { 126 if(type != null) { 127 return type; 128 } else if(symbol != null && symbol.isScope()) { 129 return Type.OBJECT; 130 } 131 return Type.UNDEFINED; 132 } 133 134 /** 135 * Assist in IR navigation. 136 * 137 * @param visitor IR navigating visitor. 138 */ 139 @Override 140 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 141 if (visitor.enterIdentNode(this)) { 142 return visitor.leaveIdentNode(this); 143 } 144 145 return this; 146 } 147 148 @Override 149 public void toString(final StringBuilder sb, final boolean printType) { 150 if (printType) { 151 optimisticTypeToString(sb, symbol == null || !symbol.hasSlot()); 152 } 153 sb.append(name); 154 } 155 156 /** 157 * Get the name of the identifier 158 * @return IdentNode name 159 */ 160 public String getName() { 161 return name; 162 } 163 164 @Override 165 public String getPropertyName() { 166 return getName(); 167 } 168 169 @Override 170 public boolean isLocal() { 171 return !getSymbol().isScope(); 172 } 173 174 /** 175 * Return the Symbol the compiler has assigned to this identifier. The symbol is a description of the storage 176 * location for the identifier. 177 * 178 * @return the symbol 179 */ 180 public Symbol getSymbol() { 181 return symbol; 182 } 183 184 /** 185 * Assign a symbol to this identifier. See {@link IdentNode#getSymbol()} for explanation of what a symbol is. 186 * 187 * @param symbol the symbol 188 * @return new node 189 */ 190 public IdentNode setSymbol(final Symbol symbol) { 191 if (this.symbol == symbol) { 192 return this; 193 } 194 final IdentNode newIdent = (IdentNode)clone(); 195 newIdent.symbol = symbol; 196 return newIdent; 197 } 198 199 /** 200 * Check if this IdentNode is a property name 201 * @return true if this is a property name 202 */ 203 public boolean isPropertyName() { 204 return (flags & PROPERTY_NAME) == PROPERTY_NAME; 205 } 206 207 /** 208 * Flag this IdentNode as a property name 209 * @return a node equivalent to this one except for the requested change. 210 */ 211 public IdentNode setIsPropertyName() { 212 if (isPropertyName()) { 213 return this; 214 } 215 return new IdentNode(this, name, type, flags | PROPERTY_NAME, programPoint, conversion); 216 } 217 218 /** 219 * Check if this IdentNode is a future strict name 220 * @return true if this is a future strict name 221 */ 222 public boolean isFutureStrictName() { 223 return (flags & FUTURESTRICT_NAME) == FUTURESTRICT_NAME; 224 } 225 226 /** 227 * Flag this IdentNode as a future strict name 228 * @return a node equivalent to this one except for the requested change. 229 */ 230 public IdentNode setIsFutureStrictName() { 231 if (isFutureStrictName()) { 232 return this; 233 } 234 return new IdentNode(this, name, type, flags | FUTURESTRICT_NAME, programPoint, conversion); 235 } 236 237 /** 238 * Helper function for local def analysis. 239 * @return true if IdentNode is initialized on creation 240 */ 241 public boolean isInitializedHere() { 242 return (flags & INITIALIZED_HERE) == INITIALIZED_HERE; 243 } 244 245 /** 246 * Flag IdentNode to be initialized on creation 247 * @return a node equivalent to this one except for the requested change. 248 */ 249 public IdentNode setIsInitializedHere() { 250 if (isInitializedHere()) { 251 return this; 252 } 253 return new IdentNode(this, name, type, flags | INITIALIZED_HERE, programPoint, conversion); 254 } 255 256 /** 257 * Is this a LET or CONST identifier used before its declaration? 258 * 259 * @return true if identifier is dead 260 */ 261 public boolean isDead() { 262 return (flags & IS_DEAD) != 0; 263 } 264 265 /** 266 * Flag this IdentNode as a LET or CONST identifier used before its declaration. 267 * 268 * @return a new IdentNode equivalent to this but marked as dead. 269 */ 270 public IdentNode markDead() { 271 return new IdentNode(this, name, type, flags | IS_DEAD, programPoint, conversion); 272 } 273 274 /** 275 * Is this IdentNode declared here? 276 * 277 * @return true if identifier is declared here 278 */ 279 public boolean isDeclaredHere() { 280 return (flags & IS_DECLARED_HERE) != 0; 281 } 282 283 /** 284 * Flag this IdentNode as being declared here. 285 * 286 * @return a new IdentNode equivalent to this but marked as declared here. 287 */ 288 public IdentNode setIsDeclaredHere() { 289 if (isDeclaredHere()) { 290 return this; 291 } 292 return new IdentNode(this, name, type, flags | IS_DECLARED_HERE, programPoint, conversion); 293 } 294 295 /** 296 * Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and 297 * __LINE__). 298 * 299 * @return true if this IdentNode's name is same as that of a compile-time property 300 */ 301 public boolean isCompileTimePropertyName() { 302 return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName()); 303 } 304 305 @Override 306 public boolean isFunction() { 307 return (flags & FUNCTION) == FUNCTION; 308 } 309 310 @Override 311 public IdentNode setType(final Type type) { 312 if (this.type == type) { 313 return this; 314 } 315 return new IdentNode(this, name, type, flags, programPoint, conversion); 316 } 317 318 /** 319 * Mark this node as being the callee operand of a {@link CallNode}. 320 * @return an ident node identical to this one in all aspects except with its function flag set. 321 */ 322 public IdentNode setIsFunction() { 323 if (isFunction()) { 324 return this; 325 } 326 return new IdentNode(this, name, type, flags | FUNCTION, programPoint, conversion); 327 } 328 329 /** 330 * Mark this node as not being the callee operand of a {@link CallNode}. 331 * @return an ident node identical to this one in all aspects except with its function flag unset. 332 */ 333 public IdentNode setIsNotFunction() { 334 if (! isFunction()) { 335 return this; 336 } 337 return new IdentNode(this, name, type, flags & ~FUNCTION, programPoint, conversion); 338 } 339 340 @Override 341 public int getProgramPoint() { 342 return programPoint; 343 } 344 345 @Override 346 public Optimistic setProgramPoint(final int programPoint) { 347 if (this.programPoint == programPoint) { 348 return this; 349 } 350 return new IdentNode(this, name, type, flags, programPoint, conversion); 351 } 352 353 @Override 354 public Type getMostOptimisticType() { 355 return Type.INT; 356 } 357 358 @Override 359 public Type getMostPessimisticType() { 360 return Type.OBJECT; 361 } 362 363 @Override 364 public boolean canBeOptimistic() { 365 return true; 366 } 367 368 @Override 369 public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) { 370 if(this.conversion == conversion) { 371 return this; 372 } 373 return new IdentNode(this, name, type, flags, programPoint, conversion); 374 } 375 376 /** 377 * Is this an internal symbol, i.e. one that starts with ':'. Those can 378 * never be optimistic. 379 * @return true if internal symbol 380 */ 381 public boolean isInternal() { 382 assert name != null; 383 return name.charAt(0) == ':'; 384 } 385 386 @Override 387 public LocalVariableConversion getLocalVariableConversion() { 388 return conversion; 389 } 390 391 /** 392 * Checks if this is a direct super identifier 393 * 394 * @return true if the direct super flag is set 395 */ 396 public boolean isDirectSuper() { 397 return (flags & DIRECT_SUPER) != 0; 398 } 399 400 /** 401 * Return a new identifier with the direct super flag set. 402 * 403 * @return the new identifier 404 */ 405 public IdentNode setIsDirectSuper() { 406 return new IdentNode(this, name, type, flags | DIRECT_SUPER, programPoint, conversion); 407 } 408 409 /** 410 * Checks if this is a rest parameter 411 * 412 * @return true if the rest parameter flag is set 413 */ 414 public boolean isRestParameter() { 415 return (flags & REST_PARAMETER) != 0; 416 } 417 418 /** 419 * Return a new identifier with the rest parameter flag set. 420 * 421 * @return the new identifier 422 */ 423 public IdentNode setIsRestParameter() { 424 return new IdentNode(this, name, type, flags | REST_PARAMETER, programPoint, conversion); 425 } 426 427 /** 428 * Checks if this is a proto property name. 429 * 430 * @return true if this is the proto property name 431 */ 432 public boolean isProtoPropertyName() { 433 return (flags & PROTO_PROPERTY) != 0; 434 } 435 436 /** 437 * Return a new identifier with the proto property name flag set. 438 * 439 * @return the new identifier 440 */ 441 public IdentNode setIsProtoPropertyName() { 442 return new IdentNode(this, name, type, flags | PROTO_PROPERTY, programPoint, conversion); 443 } 444 445 /** 446 * Checks whether this is a default parameter. 447 * 448 * @return true if this is a default parameter 449 */ 450 public boolean isDefaultParameter() { 451 return (flags & DEFAULT_PARAMETER) != 0; 452 } 453 454 /** 455 * Return a new identifier with the default parameter flag set. 456 * 457 * @return the new identifier 458 */ 459 public IdentNode setIsDefaultParameter() { 460 return new IdentNode(this, name, type, flags | DEFAULT_PARAMETER, programPoint, conversion); 461 } 462 463 /** 464 * Checks whether this is a destructured parameter. 465 * 466 * @return true if this is a destructured parameter 467 */ 468 public boolean isDestructuredParameter() { 469 return (flags & DESTRUCTURED_PARAMETER) != 0; 470 } 471 472 /** 473 * Return a new identifier with the destructured parameter flag set. 474 * 475 * @return the new identifier 476 */ 477 public IdentNode setIsDestructuredParameter() { 478 return new IdentNode(this, name, type, flags | DESTRUCTURED_PARAMETER, programPoint, conversion); 479 } 480 }