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