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