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     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
 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
 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
 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
 154     public boolean hasGoto() {
 155         return !isForInOrOf() && test == null;
 156     }
 157 
 158     @Override
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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
 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
 271     public Block getBody() {
 272         return body;
 273     }
 274 
 275     @Override
 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
 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
 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
 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      */
 308     public boolean needsScopeCreator() {
 309         return isForInOrOf() && hasPerIterationScope();
 310     }
 311 }