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