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