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 
26 package jdk.nashorn.internal.ir;
27 
28 import jdk.nashorn.internal.ir.annotations.Immutable;
29 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
30 
31 /**
32  * IR representing a FOR statement.
33  */
34 @Immutable
35 public 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      */
ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags)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      */
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)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 
ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, final Block body, final JoinPredecessorExpression modify, final int flags, final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator)95     private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
96             final Block body, final JoinPredecessorExpression modify, final int flags,
97             final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
98         super(forNode, test, body, controlFlowEscapes, conversion);
99         this.init   = init;
100         this.modify = modify;
101         this.flags  = flags;
102         this.iterator = iterator;
103     }
104 
105     @Override
ensureUniqueLabels(final LexicalContext lc)106     public Node ensureUniqueLabels(final LexicalContext lc) {
107         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
108     }
109 
110     @Override
accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor)111     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
112         if (visitor.enterForNode(this)) {
113             return visitor.leaveForNode(
114                 setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
115                 setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
116                 setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
117                 setBody(lc, (Block)body.accept(visitor)));
118         }
119 
120         return this;
121     }
122 
123     @Override
toString(final StringBuilder sb, final boolean printTypes)124     public void toString(final StringBuilder sb, final boolean printTypes) {
125         sb.append("for");
126         LocalVariableConversion.toString(conversion, sb).append(' ');
127 
128         if (isForIn()) {
129             init.toString(sb, printTypes);
130             sb.append(" in ");
131             modify.toString(sb, printTypes);
132         } else if (isForOf()) {
133             init.toString(sb, printTypes);
134             sb.append(" of ");
135             modify.toString(sb, printTypes);
136         } else {
137             if (init != null) {
138                 init.toString(sb, printTypes);
139             }
140             sb.append("; ");
141             if (test != null) {
142                 test.toString(sb, printTypes);
143             }
144             sb.append("; ");
145             if (modify != null) {
146                 modify.toString(sb, printTypes);
147             }
148         }
149 
150         sb.append(')');
151     }
152 
153     @Override
hasGoto()154     public boolean hasGoto() {
155         return !isForInOrOf() && test == null;
156     }
157 
158     @Override
mustEnter()159     public boolean mustEnter() {
160         if (isForInOrOf()) {
161             return false; //may be an empty set to iterate over, then we skip the loop
162         }
163         return test == null;
164     }
165 
166     /**
167      * Get the initialization expression for this for loop
168      * @return the initialization expression
169      */
getInit()170     public Expression getInit() {
171         return init;
172     }
173 
174     /**
175      * Reset the initialization expression for this for loop
176      * @param lc lexical context
177      * @param init new initialization expression
178      * @return new for node if changed or existing if not
179      */
setInit(final LexicalContext lc, final Expression init)180     public ForNode setInit(final LexicalContext lc, final Expression init) {
181         if (this.init == init) {
182             return this;
183         }
184         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
185     }
186 
187     /**
188      * Is this a for in construct rather than a standard init;condition;modification one
189      * @return true if this is a for in constructor
190      */
isForIn()191     public boolean isForIn() {
192         return (flags & IS_FOR_IN) != 0;
193     }
194 
195     /**
196      * Is this a for-of loop?
197      * @return true if this is a for-of loop
198      */
isForOf()199     public boolean isForOf() {
200         return (flags & IS_FOR_OF) != 0;
201     }
202 
203     /**
204      * Is this a for-in or for-of statement?
205      * @return true if this is a for-in or for-of loop
206      */
isForInOrOf()207     public boolean isForInOrOf() {
208         return isForIn() || isForOf();
209     }
210 
211     /**
212      * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
213      * in ECMAScript 6
214      * @return true if this is a for each construct
215      */
isForEach()216     public boolean isForEach() {
217         return (flags & IS_FOR_EACH) != 0;
218     }
219 
220     /**
221      * If this is a for in or for each construct, there is an iterator symbol
222      * @return the symbol for the iterator to be used, or null if none exists
223      */
getIterator()224     public Symbol getIterator() {
225         return iterator;
226     }
227 
228     /**
229      * Assign an iterator symbol to this ForNode. Used for for in and for each constructs
230      * @param lc the current lexical context
231      * @param iterator the iterator symbol
232      * @return a ForNode with the iterator set
233      */
setIterator(final LexicalContext lc, final Symbol iterator)234     public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
235         if (this.iterator == iterator) {
236             return this;
237         }
238         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
239     }
240 
241     /**
242      * Get the modification expression for this ForNode
243      * @return the modification expression
244      */
getModify()245     public JoinPredecessorExpression getModify() {
246         return modify;
247     }
248 
249     /**
250      * Reset the modification expression for this ForNode
251      * @param lc lexical context
252      * @param modify new modification expression
253      * @return new for node if changed or existing if not
254      */
setModify(final LexicalContext lc, final JoinPredecessorExpression modify)255     public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
256         if (this.modify == modify) {
257             return this;
258         }
259         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
260     }
261 
262     @Override
setTest(final LexicalContext lc, final JoinPredecessorExpression test)263     public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
264         if (this.test == test) {
265             return this;
266         }
267         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
268     }
269 
270     @Override
getBody()271     public Block getBody() {
272         return body;
273     }
274 
275     @Override
setBody(final LexicalContext lc, final Block body)276     public ForNode setBody(final LexicalContext lc, final Block body) {
277         if (this.body == body) {
278             return this;
279         }
280         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
281     }
282 
283     @Override
setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes)284     public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
285         if (this.controlFlowEscapes == controlFlowEscapes) {
286             return this;
287         }
288         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
289     }
290 
291     @Override
setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion)292     JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
293         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
294     }
295 
296     @Override
hasPerIterationScope()297     public boolean hasPerIterationScope() {
298         return (flags & PER_ITERATION_SCOPE) != 0;
299     }
300 
301     /**
302      * Returns true if this for-node needs the scope creator of its containing block to create
303      * per-iteration scope. This is only true for for-in loops with lexical declarations.
304      *
305      * @see Block#providesScopeCreator()
306      * @return true if the containing block's scope object creator is required in codegen
307      */
needsScopeCreator()308     public boolean needsScopeCreator() {
309         return isForInOrOf() && hasPerIterationScope();
310     }
311 }
312