VarNode.java revision 1336:efb5f54092ed
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;
30
31/**
32 * Node represents a var/let declaration.
33 */
34@Immutable
35public 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}
294