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 }