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 
  96     private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
  97             final Block body, final JoinPredecessorExpression modify, final int flags,
  98             final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
  99         super(forNode, test, body, controlFlowEscapes, conversion);
 100         this.init   = init;
 101         this.modify = modify;
 102         this.flags  = flags;
 103         this.iterator = iterator;
 104     }
 105 
 106     @Override
 107     public Node ensureUniqueLabels(final LexicalContext lc) {
 108         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 109     }
 110 
 111     @Override
 112     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 113         if (visitor.enterForNode(this)) {
 114             return visitor.leaveForNode(
 115                 setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
 116                 setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
 117                 setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
 118                 setBody(lc, (Block)body.accept(visitor)));
 119         }
 120 
 121         return this;
 122     }
 123 
 124     @Override
 125     public void toString(final StringBuilder sb, final boolean printTypes) {
 126         sb.append("for");
 127         LocalVariableConversion.toString(conversion, sb).append(' ');
 128 
 129         if (isForIn()) {
 130             init.toString(sb, printTypes);
 131             sb.append(" in ");
 132             modify.toString(sb, printTypes);
 133         } else if (isForOf()) {
 134             init.toString(sb, printTypes);
 135             sb.append(" of ");
 136             modify.toString(sb, printTypes);
 137         } else {
 138             if (init != null) {
 139                 init.toString(sb, printTypes);
 140             }
 141             sb.append("; ");
 142             if (test != null) {
 143                 test.toString(sb, printTypes);
 144             }
 145             sb.append("; ");
 146             if (modify != null) {
 147                 modify.toString(sb, printTypes);
 148             }
 149         }
 150 
 151         sb.append(')');
 152     }
 153 
 154     @Override
 155     public boolean hasGoto() {
 156         return !isForInOrOf() && test == null;
 157     }
 158 
 159     @Override
 160     public boolean mustEnter() {
 161         if (isForInOrOf()) {
 162             return false; //may be an empty set to iterate over, then we skip the loop
 163         }
 164         return test == null;
 165     }
 166 
 167     /**
 168      * Get the initialization expression for this for loop
 169      * @return the initialization expression
 170      */
 171     public Expression getInit() {
 172         return init;
 173     }
 174 
 175     /**
 176      * Reset the initialization expression for this for loop
 177      * @param lc lexical context
 178      * @param init new initialization expression
 179      * @return new for node if changed or existing if not
 180      */
 181     public ForNode setInit(final LexicalContext lc, final Expression init) {
 182         if (this.init == init) {
 183             return this;
 184         }
 185         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 186     }
 187 
 188     /**
 189      * Is this a for in construct rather than a standard init;condition;modification one
 190      * @return true if this is a for in constructor
 191      */
 192     public boolean isForIn() {
 193         return (flags & IS_FOR_IN) != 0;
 194     }
 195 
 196     /**
 197      * Is this a for-of loop?
 198      * @return true if this is a for-of loop
 199      */
 200     public boolean isForOf() {
 201         return (flags & IS_FOR_OF) != 0;
 202     }
 203 
 204     /**
 205      * Is this a for-in or for-of statement?
 206      * @return true if this is a for-in or for-of loop
 207      */
 208     public boolean isForInOrOf() {
 209         return isForIn() || isForOf();
 210     }
 211 
 212     /**
 213      * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
 214      * in ECMAScript 6
 215      * @return true if this is a for each construct
 216      */
 217     public boolean isForEach() {
 218         return (flags & IS_FOR_EACH) != 0;
 219     }
 220 
 221     /**
 222      * If this is a for in or for each construct, there is an iterator symbol
 223      * @return the symbol for the iterator to be used, or null if none exists
 224      */
 225     public Symbol getIterator() {
 226         return iterator;
 227     }
 228 
 229     /**
 230      * Assign an iterator symbol to this ForNode. Used for for in and for each constructs
 231      * @param lc the current lexical context
 232      * @param iterator the iterator symbol
 233      * @return a ForNode with the iterator set
 234      */
 235     public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
 236         if (this.iterator == iterator) {
 237             return this;
 238         }
 239         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 240     }
 241 
 242     /**
 243      * Get the modification expression for this ForNode
 244      * @return the modification expression
 245      */
 246     public JoinPredecessorExpression getModify() {
 247         return modify;
 248     }
 249 
 250     /**
 251      * Reset the modification expression for this ForNode
 252      * @param lc lexical context
 253      * @param modify new modification expression
 254      * @return new for node if changed or existing if not
 255      */
 256     public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
 257         if (this.modify == modify) {
 258             return this;
 259         }
 260         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 261     }
 262 
 263     @Override
 264     public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
 265         if (this.test == test) {
 266             return this;
 267         }
 268         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 269     }
 270 
 271     @Override
 272     public Block getBody() {
 273         return body;
 274     }
 275 
 276     @Override
 277     public ForNode setBody(final LexicalContext lc, final Block body) {
 278         if (this.body == body) {
 279             return this;
 280         }
 281         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 282     }
 283 
 284     @Override
 285     public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
 286         if (this.controlFlowEscapes == controlFlowEscapes) {
 287             return this;
 288         }
 289         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 290     }
 291 
 292     @Override
 293     JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
 294         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 295     }
 296 
 297     @Override
 298     public boolean hasPerIterationScope() {
 299         return (flags & PER_ITERATION_SCOPE) != 0;
 300     }
 301 
 302     /**
 303      * Returns true if this for-node needs the scope creator of its containing block to create
 304      * per-iteration scope. This is only true for for-in loops with lexical declarations.
 305      *
 306      * @see Block#providesScopeCreator()
 307      * @return true if the containing block's scope object creator is required in codegen
 308      */
 309     public boolean needsScopeCreator() {
 310         return isForInOrOf() && hasPerIterationScope();
 311     }
 312 }