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