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 jdk.nashorn.internal.ir.annotations.Immutable;
  29 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  30 import jdk.nashorn.internal.parser.Token;
  31 
  32 /**
  33  * Node represents a var/let declaration.
  34  */
  35 @Immutable
  36 public final class VarNode extends Statement implements Assignment<IdentNode> {
  37     private static final long serialVersionUID = 1L;
  38 
  39     /** Var name. */
  40     private final IdentNode name;
  41 
  42     /** Initialization expression. */
  43     private final Expression init;
  44 
  45     /** Is this a var statement (as opposed to a "var" in a for loop statement) */
  46     private final int flags;
  47 
  48     /** Flag for ES6 LET declaration */
  49     public static final int IS_LET                       = 1 << 0;
  50 
  51     /** Flag for ES6 CONST declaration */
  52     public static final int IS_CONST                     = 1 << 1;
  53 
  54     /** Flag that determines if this is the last function declaration in a function
  55      *  This is used to micro optimize the placement of return value assignments for
  56      *  a program node */
  57     public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 2;
  58 
  59     /**
  60      * Constructor
  61      *
  62      * @param lineNumber line number
  63      * @param token      token
  64      * @param finish     finish
  65      * @param name       name of variable
  66      * @param init       init node or null if just a declaration
  67      */
  68     public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init) {
  69         this(lineNumber, token, finish, name, init, 0);
  70     }
  71 
  72     private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) {
  73         super(varNode);
  74         this.name = init == null ? name : name.setIsInitializedHere();
  75         this.init = init;
  76         this.flags = flags;
  77     }
  78 
  79     /**
  80      * Constructor
  81      *
  82      * @param lineNumber line number
  83      * @param token      token
  84      * @param finish     finish
  85      * @param name       name of variable
  86      * @param init       init node or null if just a declaration
  87      * @param flags      flags
  88      */
  89     public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init, final int flags) {
  90         super(lineNumber, token, finish);
  91 
  92         this.name  = init == null ? name : name.setIsInitializedHere();
  93         this.init  = init;
  94         this.flags = flags;
  95     }
  96 
  97     @Override
  98     public boolean isAssignment() {
  99         return hasInit();
 100     }
 101 
 102     @Override
 103     public IdentNode getAssignmentDest() {
 104         return isAssignment() ? name : null;
 105     }
 106 
 107     @Override
 108     public VarNode setAssignmentDest(final IdentNode n) {
 109         return setName(n);
 110     }
 111 
 112     @Override
 113     public Expression getAssignmentSource() {
 114         return isAssignment() ? getInit() : null;
 115     }
 116 
 117     /**
 118      * Is this a VAR node block scoped? This returns true for ECMAScript 6 LET and CONST nodes.
 119      * @return true if an ES6 LET or CONST node
 120      */
 121     public boolean isBlockScoped() {
 122         return getFlag(IS_LET) || getFlag(IS_CONST);
 123     }
 124 
 125     /**
 126      * Is this an ECMAScript 6 LET node?
 127      * @return true if LET node
 128      */
 129     public boolean isLet() {
 130         return getFlag(IS_LET);
 131     }
 132 
 133     /**
 134      * Is this an ECMAScript 6 CONST node?
 135      * @return true if CONST node
 136      */
 137     public boolean isConst() {
 138         return getFlag(IS_CONST);
 139     }
 140 
 141     /**
 142      * Return the flags to use for symbols for this declaration.
 143      * @return the symbol flags
 144      */
 145     public int getSymbolFlags() {
 146         if (isLet()) {
 147             return Symbol.IS_VAR | Symbol.IS_LET;
 148         } else if (isConst()) {
 149             return Symbol.IS_VAR | Symbol.IS_CONST;
 150         }
 151         return Symbol.IS_VAR;
 152     }
 153 
 154     /**
 155      * Does this variable declaration have an init value
 156      * @return true if an init exists, false otherwise
 157      */
 158     public boolean hasInit() {
 159         return init != null;
 160     }
 161 
 162     /**
 163      * Assist in IR navigation.
 164      * @param visitor IR navigating visitor.
 165      */
 166     @Override
 167     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 168         if (visitor.enterVarNode(this)) {
 169             // var is right associative, so visit init before name
 170             final Expression newInit = init == null ? null : (Expression)init.accept(visitor);
 171             final IdentNode  newName = (IdentNode)name.accept(visitor);
 172             final VarNode    newThis;
 173             if (name != newName || init != newInit) {
 174                 newThis = new VarNode(this, newName, newInit, flags);
 175             } else {
 176                 newThis = this;
 177             }
 178             return visitor.leaveVarNode(newThis);
 179         }
 180         return this;
 181     }
 182 
 183     @Override
 184     public void toString(final StringBuilder sb, final boolean printType) {
 185         sb.append(Token.descType(getToken()).getName()).append(' ');
 186         name.toString(sb, printType);
 187 
 188         if (init != null) {
 189             sb.append(" = ");
 190             init.toString(sb, printType);
 191         }
 192     }
 193 
 194     /**
 195      * If this is an assignment of the form {@code var x = init;}, get the init part.
 196      * @return the expression to initialize the variable to, null if just a declaration
 197      */
 198     public Expression getInit() {
 199         return init;
 200     }
 201 
 202     /**
 203      * Reset the initialization expression
 204      * @param init new initialization expression
 205      * @return a node equivalent to this one except for the requested change.
 206      */
 207     public VarNode setInit(final Expression init) {
 208         if (this.init == init) {
 209             return this;
 210         }
 211         return new VarNode(this, name, init, flags);
 212     }
 213 
 214     /**
 215      * Get the identifier for the variable
 216      * @return IdentNode representing the variable being set or declared
 217      */
 218     public IdentNode getName() {
 219         return name;
 220     }
 221 
 222     /**
 223      * Reset the identifier for this VarNode
 224      * @param name new IdentNode representing the variable being set or declared
 225      * @return a node equivalent to this one except for the requested change.
 226      */
 227     public VarNode setName(final IdentNode name) {
 228         if (this.name == name) {
 229             return this;
 230         }
 231         return new VarNode(this, name, init, flags);
 232     }
 233 
 234     private VarNode setFlags(final int flags) {
 235         if (this.flags == flags) {
 236             return this;
 237         }
 238         return new VarNode(this, name, init, flags);
 239     }
 240 
 241     /**
 242      * Check if a flag is set for this var node
 243      * @param flag flag
 244      * @return true if flag is set
 245      */
 246     public boolean getFlag(final int flag) {
 247         return (flags & flag) == flag;
 248     }
 249 
 250     /**
 251      * Set a flag for this var node
 252      * @param flag flag
 253      * @return new node if flags changed, same otherwise
 254      */
 255     public VarNode setFlag(final int flag) {
 256         return setFlags(flags | flag);
 257     }
 258 
 259     /**
 260      * Returns true if this is a function declaration.
 261      * @return true if this is a function declaration.
 262      */
 263     public boolean isFunctionDeclaration() {
 264         return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
 265     }
 266 
 267     /**
 268      * Returns true if this is an anonymous function declaration.
 269      * @return true if this is an anonymous function declaration.
 270      */
 271     public boolean isAnonymousFunctionDeclaration() {
 272         return isFunctionDeclaration() && ((FunctionNode)init).isAnonymous();
 273     }
 274 }