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 */ 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 }