ForNode.java revision 1399:eea9202e8930
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    /** Does this loop need a per-iteration scope because its init contain a LET declaration? */
55    public static final int PER_ITERATION_SCOPE = 1 << 2;
56
57    private final int flags;
58
59    /**
60     * Constructs a ForNode
61     *
62     * @param lineNumber The line number of header
63     * @param token      The for token
64     * @param finish     The last character of the for node
65     * @param body       The body of the for node
66     * @param flags      The flags
67     */
68    public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){
69        this(lineNumber, token, finish, body, flags, null, null, null);
70    }
71
72    /**
73     * Constructor
74     *
75     * @param lineNumber The line number of header
76     * @param token      The for token
77     * @param finish     The last character of the for node
78     * @param body       The body of the for node
79     * @param flags      The flags
80     * @param init       The initial expression
81     * @param test       The test expression
82     * @param modify     The modify expression
83     */
84    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) {
85        super(lineNumber, token, finish, body, test, false);
86        this.flags  = flags;
87        this.init = init;
88        this.modify = modify;
89        this.iterator = null;
90
91    }
92
93    private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
94            final Block body, final JoinPredecessorExpression modify, final int flags,
95            final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
96        super(forNode, test, body, controlFlowEscapes, conversion);
97        this.init   = init;
98        this.modify = modify;
99        this.flags  = flags;
100        this.iterator = iterator;
101    }
102
103    @Override
104    public Node ensureUniqueLabels(final LexicalContext lc) {
105        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
106    }
107
108    @Override
109    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
110        if (visitor.enterForNode(this)) {
111            return visitor.leaveForNode(
112                setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
113                setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
114                setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
115                setBody(lc, (Block)body.accept(visitor)));
116        }
117
118        return this;
119    }
120
121    @Override
122    public void toString(final StringBuilder sb, final boolean printTypes) {
123        sb.append("for");
124        LocalVariableConversion.toString(conversion, sb).append(' ');
125
126        if (isForIn()) {
127            init.toString(sb, printTypes);
128            sb.append(" in ");
129            modify.toString(sb, printTypes);
130        } else {
131            if (init != null) {
132                init.toString(sb, printTypes);
133            }
134            sb.append("; ");
135            if (test != null) {
136                test.toString(sb, printTypes);
137            }
138            sb.append("; ");
139            if (modify != null) {
140                modify.toString(sb, printTypes);
141            }
142        }
143
144        sb.append(')');
145    }
146
147    @Override
148    public boolean hasGoto() {
149        return !isForIn() && test == null;
150    }
151
152    @Override
153    public boolean mustEnter() {
154        if (isForIn()) {
155            return false; //may be an empty set to iterate over, then we skip the loop
156        }
157        return test == null;
158    }
159
160    /**
161     * Get the initialization expression for this for loop
162     * @return the initialization expression
163     */
164    public Expression getInit() {
165        return init;
166    }
167
168    /**
169     * Reset the initialization expression for this for loop
170     * @param lc lexical context
171     * @param init new initialization expression
172     * @return new for node if changed or existing if not
173     */
174    public ForNode setInit(final LexicalContext lc, final Expression init) {
175        if (this.init == init) {
176            return this;
177        }
178        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
179    }
180
181    /**
182     * Is this a for in construct rather than a standard init;condition;modification one
183     * @return true if this is a for in constructor
184     */
185    public boolean isForIn() {
186        return (flags & IS_FOR_IN) != 0;
187    }
188    /**
189     * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
190     * in ECMAScript 6
191     * @return true if this is a for each construct
192     */
193    public boolean isForEach() {
194        return (flags & IS_FOR_EACH) != 0;
195    }
196
197    /**
198     * If this is a for in or for each construct, there is an iterator symbol
199     * @return the symbol for the iterator to be used, or null if none exists
200     */
201    public Symbol getIterator() {
202        return iterator;
203    }
204
205    /**
206     * Assign an iterator symbol to this ForNode. Used for for in and for each constructs
207     * @param lc the current lexical context
208     * @param iterator the iterator symbol
209     * @return a ForNode with the iterator set
210     */
211    public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
212        if (this.iterator == iterator) {
213            return this;
214        }
215        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
216    }
217
218    /**
219     * Get the modification expression for this ForNode
220     * @return the modification expression
221     */
222    public JoinPredecessorExpression getModify() {
223        return modify;
224    }
225
226    /**
227     * Reset the modification expression for this ForNode
228     * @param lc lexical context
229     * @param modify new modification expression
230     * @return new for node if changed or existing if not
231     */
232    public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
233        if (this.modify == modify) {
234            return this;
235        }
236        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
237    }
238
239    @Override
240    public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
241        if (this.test == test) {
242            return this;
243        }
244        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
245    }
246
247    @Override
248    public Block getBody() {
249        return body;
250    }
251
252    @Override
253    public ForNode setBody(final LexicalContext lc, final Block body) {
254        if (this.body == body) {
255            return this;
256        }
257        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
258    }
259
260    @Override
261    public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
262        if (this.controlFlowEscapes == controlFlowEscapes) {
263            return this;
264        }
265        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
266    }
267
268    @Override
269    JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
270        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
271    }
272
273    @Override
274    public boolean hasPerIterationScope() {
275        return (flags & PER_ITERATION_SCOPE) != 0;
276    }
277}
278