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 }