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