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     /** Does this loop need a per-iteration scope because its init contain a LET declaration? */
  55     public static final int PER_ITERATION_SCOPE = 1 << 2;
  56 
  57     private final int flags;
  58 
  59     /**
  60      * Constructs a ForNode
  61      *
  62      * @param lineNumber The line number of header
  63      * @param token      The for token
  64      * @param finish     The last character of the for node
  65      * @param body       The body of the for node
  66      * @param flags      The flags
  67      */
  68     public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){
  69         this(lineNumber, token, finish, body, flags, null, null, null);
  70     }
  71 
  72     /**
  73      * Constructor
  74      *
  75      * @param lineNumber The line number of header
  76      * @param token      The for token
  77      * @param finish     The last character of the for node
  78      * @param body       The body of the for node
  79      * @param flags      The flags
  80      * @param init       The initial expression
  81      * @param test       The test expression
  82      * @param modify     The modify expression
  83      */
  84     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) {
  85         super(lineNumber, token, finish, body, test, false);
  86         this.flags  = flags;
  87         this.init = init;
  88         this.modify = modify;
  89         this.iterator = null;
  90 
  91     }
  92 
  93     private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test,
  94             final Block body, final JoinPredecessorExpression modify, final int flags,
  95             final boolean controlFlowEscapes, final LocalVariableConversion conversion, final Symbol iterator) {
  96         super(forNode, test, body, controlFlowEscapes, conversion);
  97         this.init   = init;
  98         this.modify = modify;
  99         this.flags  = flags;
 100         this.iterator = iterator;
 101     }
 102 
 103     @Override
 104     public Node ensureUniqueLabels(final LexicalContext lc) {
 105         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 106     }
 107 
 108     @Override
 109     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 110         if (visitor.enterForNode(this)) {
 111             return visitor.leaveForNode(
 112                 setInit(lc, init == null ? null : (Expression)init.accept(visitor)).
 113                 setTest(lc, test == null ? null : (JoinPredecessorExpression)test.accept(visitor)).
 114                 setModify(lc, modify == null ? null : (JoinPredecessorExpression)modify.accept(visitor)).
 115                 setBody(lc, (Block)body.accept(visitor)));
 116         }
 117 
 118         return this;
 119     }
 120 
 121     @Override
 122     public void toString(final StringBuilder sb, final boolean printTypes) {
 123         sb.append("for");
 124         LocalVariableConversion.toString(conversion, sb).append(' ');
 125 
 126         if (isForIn()) {
 127             init.toString(sb, printTypes);
 128             sb.append(" in ");
 129             modify.toString(sb, printTypes);
 130         } else {
 131             if (init != null) {
 132                 init.toString(sb, printTypes);
 133             }
 134             sb.append("; ");
 135             if (test != null) {
 136                 test.toString(sb, printTypes);
 137             }
 138             sb.append("; ");
 139             if (modify != null) {
 140                 modify.toString(sb, printTypes);
 141             }
 142         }
 143 
 144         sb.append(')');
 145     }
 146 
 147     @Override
 148     public boolean hasGoto() {
 149         return !isForIn() && test == null;
 150     }
 151 
 152     @Override
 153     public boolean mustEnter() {
 154         if (isForIn()) {
 155             return false; //may be an empty set to iterate over, then we skip the loop
 156         }
 157         return test == null;
 158     }
 159 
 160     /**
 161      * Get the initialization expression for this for loop
 162      * @return the initialization expression
 163      */
 164     public Expression getInit() {
 165         return init;
 166     }
 167 
 168     /**
 169      * Reset the initialization expression for this for loop
 170      * @param lc lexical context
 171      * @param init new initialization expression
 172      * @return new for node if changed or existing if not
 173      */
 174     public ForNode setInit(final LexicalContext lc, final Expression init) {
 175         if (this.init == init) {
 176             return this;
 177         }
 178         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 179     }
 180 
 181     /**
 182      * Is this a for in construct rather than a standard init;condition;modification one
 183      * @return true if this is a for in constructor
 184      */
 185     public boolean isForIn() {
 186         return (flags & IS_FOR_IN) != 0;
 187     }
 188     /**
 189      * Is this a for each construct, known from e.g. Rhino. This will be a for of construct
 190      * in ECMAScript 6
 191      * @return true if this is a for each construct
 192      */
 193     public boolean isForEach() {
 194         return (flags & IS_FOR_EACH) != 0;
 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 lc the current lexical context
 208      * @param iterator the iterator symbol
 209      * @return a ForNode with the iterator set
 210      */
 211     public ForNode setIterator(final LexicalContext lc, final Symbol iterator) {
 212         if (this.iterator == iterator) {
 213             return this;
 214         }
 215         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 216     }
 217 
 218     /**
 219      * Get the modification expression for this ForNode
 220      * @return the modification expression
 221      */
 222     public JoinPredecessorExpression getModify() {
 223         return modify;
 224     }
 225 
 226     /**
 227      * Reset the modification expression for this ForNode
 228      * @param lc lexical context
 229      * @param modify new modification expression
 230      * @return new for node if changed or existing if not
 231      */
 232     public ForNode setModify(final LexicalContext lc, final JoinPredecessorExpression modify) {
 233         if (this.modify == modify) {
 234             return this;
 235         }
 236         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 237     }
 238 
 239     @Override
 240     public ForNode setTest(final LexicalContext lc, final JoinPredecessorExpression test) {
 241         if (this.test == test) {
 242             return this;
 243         }
 244         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 245     }
 246 
 247     @Override
 248     public Block getBody() {
 249         return body;
 250     }
 251 
 252     @Override
 253     public ForNode setBody(final LexicalContext lc, final Block body) {
 254         if (this.body == body) {
 255             return this;
 256         }
 257         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 258     }
 259 
 260     @Override
 261     public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
 262         if (this.controlFlowEscapes == controlFlowEscapes) {
 263             return this;
 264         }
 265         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 266     }
 267 
 268     @Override
 269     JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
 270         return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion, iterator));
 271     }
 272 
 273     @Override
 274     public boolean hasPerIterationScope() {
 275         return (flags & PER_ITERATION_SCOPE) != 0;
 276     }
 277 
 278     /**
 279      * Returns true if this for-node needs the scope creator of its containing block to create
 280      * per-iteration scope. This is only true for for-in loops with lexical declarations.
 281      *
 282      * @return true if the containing block's scope object creator is required in codegen
 283      */
 284     public boolean needsScopeCreator() {
 285         return isForIn() && hasPerIterationScope();
 286     }
 287 }