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 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 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 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 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 154 public boolean hasGoto() { 155 return !isForInOrOf() && test == null; 156 } 157 158 @Override 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 271 public Block getBody() { 272 return body; 273 } 274 275 @Override 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 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 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 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 */ 308 public boolean needsScopeCreator() { 309 return isForInOrOf() && hasPerIterationScope(); 310 } 311 }