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