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