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 /** Does this loop need a per-iteration scope because its init contain a LET declaration? */ 55 public static final int PER_ITERATION_SCOPE = 1 << 2; 56 57 private final int flags; 58 59 /** 60 * Constructs a ForNode 61 * 62 * @param lineNumber The line number of header 63 * @param token The for token 64 * @param finish The last character of the for node 65 * @param body The body of the for node 66 * @param flags The flags 67 */ 68 public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){ 69 this(lineNumber, token, finish, body, flags, null, null, null); 70 } 71 72 /** 73 * Constructor 74 * 75 * @param lineNumber The line number of header 76 * @param token The for token 77 * @param finish The last character of the for node 78 * @param body The body of the for node 79 * @param flags The flags 80 * @param init The initial expression 81 * @param test The test expression 82 * @param modify The modify expression 83 */ 84 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) { 85 super(lineNumber, token, finish, body, test, false); 86 this.flags = flags; 87 this.init = init; 88 this.modify = modify; 89 this.iterator = null; 90 91 } 92 93 private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, 94 final Block body, final JoinPredecessorExpression modify, final int flags, 95 final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) { 96 super(forNode, test, body, controlFlowEscapes, conversion); 97 this.init = init; 98 this.modify = modify; 99 this.flags = flags; 100 this.iterator = iterator; 101 } 102 103 @Override 104 public Node ensureUniqueLabels(final LexicalContext lc) { 105 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 106 } 107 108 @Override 109 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 110 if (visitor.enterForNode(this)) { 111 return visitor.leaveForNode( 112 setInit(lc, init == null ? null : (Expression)init.accept(visitor)). 113 setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)). 114 setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)). 115 setBody(lc, (Block)body.accept(visitor))); 116 } 117 118 return this; 119 } 120 121 @Override 122 public void toString(final StringBuilder sb, final boolean printTypes) { 123 sb.append("for"); 124 LocalVariableConversion.toString(conversion, sb).append(' '); 125 126 if (isForIn()) { 127 init.toString(sb, printTypes); 128 sb.append(" in "); 129 modify.toString(sb, printTypes); 130 } else { 131 if (init != null) { 132 init.toString(sb, printTypes); 133 } 134 sb.append("; "); 135 if (test != null) { 136 test.toString(sb, printTypes); 137 } 138 sb.append("; "); 139 if (modify != null) { 140 modify.toString(sb, printTypes); 141 } 142 } 143 144 sb.append(')'); 145 } 146 147 @Override 148 public boolean hasGoto() { 149 return !isForIn() && test == null; 150 } 151 152 @Override 153 public boolean mustEnter() { 154 if (isForIn()) { 155 return false; //may be an empty set to iterate over, then we skip the loop 156 } 157 return test == null; 158 } 159 160 /** 161 * Get the initialization expression for this for loop 162 * @return the initialization expression 163 */ 164 public Expression getInit() { 165 return init; 166 } 167 168 /** 169 * Reset the initialization expression for this for loop 170 * @param lc lexical context 171 * @param init new initialization expression 172 * @return new for node if changed or existing if not 173 */ 174 public ForNode setInit(final LexicalContext lc, final Expression init) { 175 if (this.init == init) { 176 return this; 177 } 178 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 179 } 180 181 /** 182 * Is this a for in construct rather than a standard init;condition;modification one 183 * @return true if this is a for in constructor 184 */ 185 public boolean isForIn() { 186 return (flags & IS_FOR_IN) != 0; 187 } 188 /** 189 * Is this a for each construct, known from e.g. Rhino. This will be a for of construct 190 * in ECMAScript 6 191 * @return true if this is a for each construct 192 */ 193 public boolean isForEach() { 194 return (flags & IS_FOR_EACH) != 0; 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 lc the current lexical context 208 * @param iterator the iterator symbol 209 * @return a ForNode with the iterator set 210 */ 211 public ForNode setIterator(final LexicalContext lc, final Symbol iterator) { 212 if (this.iterator == iterator) { 213 return this; 214 } 215 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 216 } 217 218 /** 219 * Get the modification expression for this ForNode 220 * @return the modification expression 221 */ 222 public JoinPredecessorExpression getModify() { 223 return modify; 224 } 225 226 /** 227 * Reset the modification expression for this ForNode 228 * @param lc lexical context 229 * @param modify new modification expression 230 * @return new for node if changed or existing if not 231 */ 232 public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) { 233 if (this.modify == modify) { 234 return this; 235 } 236 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 237 } 238 239 @Override 240 public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) { 241 if (this.test == test) { 242 return this; 243 } 244 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 245 } 246 247 @Override 248 public Block getBody() { 249 return body; 250 } 251 252 @Override 253 public ForNode setBody(final LexicalContext lc, final Block body) { 254 if (this.body == body) { 255 return this; 256 } 257 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 258 } 259 260 @Override 261 public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) { 262 if (this.controlFlowEscapes == controlFlowEscapes) { 263 return this; 264 } 265 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 266 } 267 268 @Override 269 JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { 270 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator)); 271 } 272 273 @Override 274 public boolean hasPerIterationScope() { 275 return (flags & PER_ITERATION_SCOPE) != 0; 276 } 277 278 /** 279 * Returns true if this for-node needs the scope creator of its containing block to create 280 * per-iteration scope. This is only true for for-in loops with lexical declarations. 281 * 282 * @see Block#providesScopeCreator() 283 * @return true if the containing block's scope object creator is required in codegen 284 */ 285 public boolean needsScopeCreator() { 286 return isForIn() && hasPerIterationScope(); 287 } 288 }