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 }
--- EOF ---