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