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 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      * 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 
 277     @Override
 278     public boolean hasPerIterationScope() {
 279         return (flags & PER_ITERATION_SCOPE) != 0;
 280     }
 281 
 282     /**
 283      * Set the per-iteration-scope flag on this node.
 284      * @param lc lexical context
 285      * @return the node with flag set
 286      */
 287     public ForNode setPerIterationScope(final LexicalContext lc) {
 288         return setFlags(lc, flags | PER_ITERATION_SCOPE);
 289     }
 290 }