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 }