ForNode.java revision 953:221a84ef44c0
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    /** Initialize expression for an ordinary for statement, or the LHS expression receiving iterated-over values in a
37     * for-in statement. */
38    private final Expression init;
39
40    /** Modify expression for an ordinary statement, or the source of the iterator in the for-in statement. */
41    private final JoinPredecessorExpression modify;
42
43    /** Iterator symbol. */
44    private Symbol iterator;
45
46    /** Is this a normal for loop? */
47    public static final int IS_FOR      = 1 << 0;
48
49    /** Is this a normal for in loop? */
50    public static final int IS_FOR_IN   = 1 << 1;
51
52    /** Is this a normal for each in loop? */
53    public static final int IS_FOR_EACH = 1 << 2;
54
55    private final int flags;
56
57    /**
58     * Constructor
59     *
60     * @param lineNumber line number
61     * @param token      token
62     * @param finish     finish
63     * @param body       body
64     * @param flags      flags
65     */
66    public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags) {
67        super(lineNumber, token, finish, body, false);
68        this.flags  = flags;
69        this.init = null;
70        this.modify = null;
71    }
72
73    private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
74            final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion) {
75        super(forNode, test, body, controlFlowEscapes, conversion);
76        this.init   = init;
77        this.modify = modify;
78        this.flags  = flags;
79        // Even if the for node gets cloned in try/finally, the symbol can be shared as only one branch of the finally
80        // is executed.
81        this.iterator = forNode.iterator;
82    }
83
84    @Override
85    public Node ensureUniqueLabels(final LexicalContext lc) {
86        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
87    }
88
89    @Override
90    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
91        if (visitor.enterForNode(this)) {
92            return visitor.leaveForNode(
93                setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
94                setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
95                setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
96                setBody(lc, (Block)body.accept(visitor)));
97        }
98
99        return this;
100    }
101
102    @Override
103    public void toString(final StringBuilder sb, final boolean printTypes) {
104        sb.append("for");
105        LocalVariableConversion.toString(conversion, sb).append(' ');
106
107        if (isForIn()) {
108            init.toString(sb, printTypes);
109            sb.append(" in ");
110            modify.toString(sb, printTypes);
111        } else {
112            if (init != null) {
113                init.toString(sb, printTypes);
114            }
115            sb.append("; ");
116            if (test != null) {
117                test.toString(sb, printTypes);
118            }
119            sb.append("; ");
120            if (modify != null) {
121                modify.toString(sb, printTypes);
122            }
123        }
124
125        sb.append(')');
126    }
127
128    @Override
129    public boolean hasGoto() {
130        return !isForIn() && test == null;
131    }
132
133    @Override
134    public boolean mustEnter() {
135        if (isForIn()) {
136            return false; //may be an empty set to iterate over, then we skip the loop
137        }
138        return test == null;
139    }
140
141    /**
142     * Get the initialization expression for this for loop
143     * @return the initialization expression
144     */
145    public Expression getInit() {
146        return init;
147    }
148
149    /**
150     * Reset the initialization expression for this for loop
151     * @param lc lexical context
152     * @param init new initialization expression
153     * @return new for node if changed or existing if not
154     */
155    public ForNode setInit(final LexicalContext lc, final Expression init) {
156        if (this.init == init) {
157            return this;
158        }
159        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
160    }
161
162    /**
163     * Is this a for in construct rather than a standard init;condition;modification one
164     * @return true if this is a for in constructor
165     */
166    public boolean isForIn() {
167        return (flags & IS_FOR_IN) != 0;
168    }
169
170    /**
171     * Flag this to be a for in construct
172     * @param lc lexical context
173     * @return new for node if changed or existing if not
174     */
175    public ForNode setIsForIn(final LexicalContext lc) {
176        return setFlags(lc, flags | IS_FOR_IN);
177    }
178
179    /**
180     * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
181     * in ECMAScript 6
182     * @return true if this is a for each construct
183     */
184    public boolean isForEach() {
185        return (flags & IS_FOR_EACH) != 0;
186    }
187
188    /**
189     * Flag this to be a for each construct
190     * @param lc lexical context
191     * @return new for node if changed or existing if not
192     */
193    public ForNode setIsForEach(final LexicalContext lc) {
194        return setFlags(lc, flags | IS_FOR_EACH);
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 iterator the iterator symbol
208     */
209    public void setIterator(final Symbol iterator) {
210        this.iterator = iterator;
211    }
212
213    /**
214     * Get the modification expression for this ForNode
215     * @return the modification expression
216     */
217    public JoinPredecessorExpression getModify() {
218        return modify;
219    }
220
221    /**
222     * Reset the modification expression for this ForNode
223     * @param lc lexical context
224     * @param modify new modification expression
225     * @return new for node if changed or existing if not
226     */
227    public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
228        if (this.modify == modify) {
229            return this;
230        }
231        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
232    }
233
234    @Override
235    public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
236        if (this.test == test) {
237            return this;
238        }
239        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
240    }
241
242    @Override
243    public Block getBody() {
244        return body;
245    }
246
247    @Override
248    public ForNode setBody(final LexicalContext lc, final Block body) {
249        if (this.body == body) {
250            return this;
251        }
252        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
253    }
254
255    @Override
256    public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
257        if (this.controlFlowEscapes == controlFlowEscapes) {
258            return this;
259        }
260        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
261    }
262
263    private ForNode setFlags(final LexicalContext lc, final int flags) {
264        if (this.flags == flags) {
265            return this;
266        }
267        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
268    }
269
270    @Override
271    JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
272        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
273    }
274}
275