ForNode.java revision 1655:3ac5d360070e
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 * IR representing a FOR statement.
33 */
34@Immutable
35public final class ForNode extends LoopNode {
36    private static final long serialVersionUID = 1L;
37
38    /** Initialize expression for an ordinary for statement, or the LHS expression receiving iterated-over values in a
39     * for-in statement. */
40    private final Expression init;
41
42    /** Modify expression for an ordinary statement, or the source of the iterator in the for-in statement. */
43    private final JoinPredecessorExpression modify;
44
45    /** Iterator symbol. */
46    private final Symbol iterator;
47
48    /** Is this a normal for in loop? */
49    public static final int IS_FOR_IN           = 1 << 0;
50
51    /** Is this a normal for each in loop? */
52    public static final int IS_FOR_EACH         = 1 << 1;
53
54    /** Is this a ES6 for-of loop? */
55    public static final int IS_FOR_OF           = 1 << 2;
56
57    /** Does this loop need a per-iteration scope because its init contain a LET declaration? */
58    public static final int PER_ITERATION_SCOPE = 1 << 3;
59
60    private final int flags;
61
62    /**
63     * Constructs a ForNode
64     *
65     * @param lineNumber The line number of header
66     * @param token      The for token
67     * @param finish     The last character of the for node
68     * @param body       The body of the for node
69     * @param flags      The flags
70     */
71    public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){
72        this(lineNumber, token, finish, body, flags, null, null, null);
73    }
74
75    /**
76     * Constructor
77     *
78     * @param lineNumber The line number of header
79     * @param token      The for token
80     * @param finish     The last character of the for node
81     * @param body       The body of the for node
82     * @param flags      The flags
83     * @param init       The initial expression
84     * @param test       The test expression
85     * @param modify     The modify expression
86     */
87    public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags, final Expression init, final JoinPredecessorExpression test, final JoinPredecessorExpression modify) {
88        super(lineNumber, token, finish, body, test, false);
89        this.flags  = flags;
90        this.init = init;
91        this.modify = modify;
92        this.iterator = null;
93
94    }
95
96    private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
97            final Block body, final JoinPredecessorExpression modify, final int flags,
98            final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
99        super(forNode, test, body, controlFlowEscapes, conversion);
100        this.init   = init;
101        this.modify = modify;
102        this.flags  = flags;
103        this.iterator = iterator;
104    }
105
106    @Override
107    public Node ensureUniqueLabels(final LexicalContext lc) {
108        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
109    }
110
111    @Override
112    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
113        if (visitor.enterForNode(this)) {
114            return visitor.leaveForNode(
115                setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
116                setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
117                setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
118                setBody(lc, (Block)body.accept(visitor)));
119        }
120
121        return this;
122    }
123
124    @Override
125    public void toString(final StringBuilder sb, final boolean printTypes) {
126        sb.append("for");
127        LocalVariableConversion.toString(conversion, sb).append(' ');
128
129        if (isForIn()) {
130            init.toString(sb, printTypes);
131            sb.append(" in ");
132            modify.toString(sb, printTypes);
133        } else if (isForOf()) {
134            init.toString(sb, printTypes);
135            sb.append(" of ");
136            modify.toString(sb, printTypes);
137        } else {
138            if (init != null) {
139                init.toString(sb, printTypes);
140            }
141            sb.append("; ");
142            if (test != null) {
143                test.toString(sb, printTypes);
144            }
145            sb.append("; ");
146            if (modify != null) {
147                modify.toString(sb, printTypes);
148            }
149        }
150
151        sb.append(')');
152    }
153
154    @Override
155    public boolean hasGoto() {
156        return !isForInOrOf() && test == null;
157    }
158
159    @Override
160    public boolean mustEnter() {
161        if (isForInOrOf()) {
162            return false; //may be an empty set to iterate over, then we skip the loop
163        }
164        return test == null;
165    }
166
167    /**
168     * Get the initialization expression for this for loop
169     * @return the initialization expression
170     */
171    public Expression getInit() {
172        return init;
173    }
174
175    /**
176     * Reset the initialization expression for this for loop
177     * @param lc lexical context
178     * @param init new initialization expression
179     * @return new for node if changed or existing if not
180     */
181    public ForNode setInit(final LexicalContext lc, final Expression init) {
182        if (this.init == init) {
183            return this;
184        }
185        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
186    }
187
188    /**
189     * Is this a for in construct rather than a standard init;condition;modification one
190     * @return true if this is a for in constructor
191     */
192    public boolean isForIn() {
193        return (flags & IS_FOR_IN) != 0;
194    }
195
196    /**
197     * Is this a for-of loop?
198     * @return true if this is a for-of loop
199     */
200    public boolean isForOf() {
201        return (flags & IS_FOR_OF) != 0;
202    }
203
204    /**
205     * Is this a for-in or for-of statement?
206     * @return true if this is a for-in or for-of loop
207     */
208    public boolean isForInOrOf() {
209        return isForIn() || isForOf();
210    }
211
212    /**
213     * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
214     * in ECMAScript 6
215     * @return true if this is a for each construct
216     */
217    public boolean isForEach() {
218        return (flags & IS_FOR_EACH) != 0;
219    }
220
221    /**
222     * If this is a for in or for each construct, there is an iterator symbol
223     * @return the symbol for the iterator to be used, or null if none exists
224     */
225    public Symbol getIterator() {
226        return iterator;
227    }
228
229    /**
230     * Assign an iterator symbol to this ForNode. Used for for in and for each constructs
231     * @param lc the current lexical context
232     * @param iterator the iterator symbol
233     * @return a ForNode with the iterator set
234     */
235    public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
236        if (this.iterator == iterator) {
237            return this;
238        }
239        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
240    }
241
242    /**
243     * Get the modification expression for this ForNode
244     * @return the modification expression
245     */
246    public JoinPredecessorExpression getModify() {
247        return modify;
248    }
249
250    /**
251     * Reset the modification expression for this ForNode
252     * @param lc lexical context
253     * @param modify new modification expression
254     * @return new for node if changed or existing if not
255     */
256    public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
257        if (this.modify == modify) {
258            return this;
259        }
260        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
261    }
262
263    @Override
264    public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
265        if (this.test == test) {
266            return this;
267        }
268        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
269    }
270
271    @Override
272    public Block getBody() {
273        return body;
274    }
275
276    @Override
277    public ForNode setBody(final LexicalContext lc, final Block body) {
278        if (this.body == body) {
279            return this;
280        }
281        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
282    }
283
284    @Override
285    public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
286        if (this.controlFlowEscapes == controlFlowEscapes) {
287            return this;
288        }
289        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
290    }
291
292    @Override
293    JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
294        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
295    }
296
297    @Override
298    public boolean hasPerIterationScope() {
299        return (flags & PER_ITERATION_SCOPE) != 0;
300    }
301
302    /**
303     * Returns true if this for-node needs the scope creator of its containing block to create
304     * per-iteration scope. This is only true for for-in loops with lexical declarations.
305     *
306     * @see Block#providesScopeCreator()
307     * @return true if the containing block's scope object creator is required in codegen
308     */
309    public boolean needsScopeCreator() {
310        return isForInOrOf() && hasPerIterationScope();
311    }
312}
313