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 static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  29 
  30 import java.io.Serializable;
  31 import java.util.Collections;
  32 import java.util.List;
  33 import jdk.nashorn.internal.codegen.types.Type;
  34 import jdk.nashorn.internal.ir.annotations.Ignore;
  35 import jdk.nashorn.internal.ir.annotations.Immutable;
  36 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  37 
  38 /**
  39  * IR representation for a function call.
  40  */
  41 @Immutable
  42 public final class CallNode extends LexicalContextExpression implements Optimistic {
  43     private static final long serialVersionUID = 1L;
  44 
  45     /** Function identifier or function body. */
  46     private final Expression function;
  47 
  48     /** Call arguments. */
  49     private final List<Expression> args;
  50 
  51     /** Is this a "new" operation */
  52     private static final int IS_NEW = 1 << 0;
  53 
  54     /** Can this be a Function.call? */
  55     private static final int IS_APPLY_TO_CALL = 1 << 1;
  56 
  57     private final int flags;
  58 
  59     private final int lineNumber;
  60 
  61     private final int programPoint;
  62 
  63     private final Type optimisticType;
  64 
  65     /**
  66      * Arguments to be passed to builtin {@code eval} function
  67      */
  68     public static class EvalArgs implements Serializable {
  69         private static final long serialVersionUID = 1L;
  70         private final List<Expression> args;
  71 
  72         /** location string for the eval call */
  73         private final String location;
  74 
  75         /**
  76          * Constructor
  77          *
  78          * @param args     arguments to eval
  79          * @param location location for the eval call
  80          */
  81         public EvalArgs(final List<Expression> args, final String location) {
  82             this.args = args;
  83             this.location = location;
  84         }
  85 
  86         /**
  87          * Return the code that is to be eval:ed by this eval function
  88          * @return code as an AST node
  89          */
  90         public List<Expression> getArgs() {
  91             return Collections.unmodifiableList(args);
  92         }
  93 
  94         private EvalArgs setArgs(final List<Expression> args) {
  95             if (this.args == args) {
  96                 return this;
  97             }
  98             return new EvalArgs(args, location);
  99         }
 100 
 101         /**
 102          * Get the human readable location for this eval call
 103          * @return the location
 104          */
 105         public String getLocation() {
 106             return this.location;
 107         }
 108     }
 109 
 110     /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
 111     @Ignore
 112     private final EvalArgs evalArgs;
 113 
 114     /**
 115      * Constructors
 116      *
 117      * @param lineNumber line number
 118      * @param token      token
 119      * @param finish     finish
 120      * @param function   the function to call
 121      * @param args       args to the call
 122      * @param isNew      true if this is a constructor call with the "new" keyword
 123      */
 124     public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args, final boolean isNew) {
 125         super(token, finish);
 126 
 127         this.function       = function;
 128         this.args           = args;
 129         this.flags          = isNew ? IS_NEW : 0;
 130         this.evalArgs       = null;
 131         this.lineNumber     = lineNumber;
 132         this.programPoint   = INVALID_PROGRAM_POINT;
 133         this.optimisticType = null;
 134     }
 135 
 136     private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) {
 137         super(callNode);
 138         this.lineNumber = callNode.lineNumber;
 139         this.function = function;
 140         this.args = args;
 141         this.flags = flags;
 142         this.evalArgs = evalArgs;
 143         this.programPoint = programPoint;
 144         this.optimisticType = optimisticType;
 145     }
 146 
 147     /**
 148      * Returns the line number.
 149      * @return the line number.
 150      */
 151     public int getLineNumber() {
 152         return lineNumber;
 153     }
 154 
 155     @Override
 156     public Type getType() {
 157         return optimisticType == null ? Type.OBJECT : optimisticType;
 158     }
 159 
 160     @Override
 161     public Optimistic setType(final Type optimisticType) {
 162         if (this.optimisticType == optimisticType) {
 163             return this;
 164         }
 165         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 166     }
 167 
 168     /**
 169      * Assist in IR navigation.
 170      *
 171      * @param visitor IR navigating visitor.
 172      *
 173      * @return node or replacement
 174      */
 175     @Override
 176     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
 177         if (visitor.enterCallNode(this)) {
 178             final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
 179                     setFunction((Expression)function.accept(visitor)).
 180                     setArgs(Node.accept(visitor, args)).
 181                     setEvalArgs(evalArgs == null ?
 182                             null :
 183                             evalArgs.setArgs(Node.accept(visitor, evalArgs.getArgs()))));
 184             // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
 185             // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
 186             if (this != newCallNode) {
 187                 return Node.replaceInLexicalContext(lc, this, newCallNode);
 188             }
 189         }
 190 
 191         return this;
 192     }
 193 
 194     @Override
 195     public void toString(final StringBuilder sb, final boolean printType) {
 196         if (printType) {
 197             optimisticTypeToString(sb);
 198         }
 199 
 200         final StringBuilder fsb = new StringBuilder();
 201         function.toString(fsb, printType);
 202 
 203         if (isApplyToCall()) {
 204             sb.append(fsb.toString().replace("apply", "[apply => call]"));
 205         } else {
 206             sb.append(fsb);
 207         }
 208 
 209         sb.append('(');
 210 
 211         boolean first = true;
 212 
 213         for (final Node arg : args) {
 214             if (!first) {
 215                 sb.append(", ");
 216             } else {
 217                 first = false;
 218             }
 219 
 220             arg.toString(sb, printType);
 221         }
 222 
 223         sb.append(')');
 224     }
 225 
 226     /**
 227      * Get the arguments for the call
 228      * @return a list of arguments
 229      */
 230     public List<Expression> getArgs() {
 231         return Collections.unmodifiableList(args);
 232     }
 233 
 234     /**
 235      * Reset the arguments for the call
 236      * @param args new arguments list
 237      * @return new callnode, or same if unchanged
 238      */
 239     public CallNode setArgs(final List<Expression> args) {
 240         if (this.args == args) {
 241             return this;
 242         }
 243         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 244     }
 245 
 246     /**
 247      * If this call is an {@code eval} call, get its EvalArgs structure
 248      * @return EvalArgs for call
 249      */
 250     public EvalArgs getEvalArgs() {
 251         return evalArgs;
 252     }
 253 
 254     /**
 255      * Set the EvalArgs structure for this call, if it has been determined it is an
 256      * {@code eval}
 257      *
 258      * @param evalArgs eval args
 259      * @return same node or new one on state change
 260      */
 261     public CallNode setEvalArgs(final EvalArgs evalArgs) {
 262         if (this.evalArgs == evalArgs) {
 263             return this;
 264         }
 265         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 266     }
 267 
 268     /**
 269      * Check if this call is a call to {@code eval}
 270      * @return true if this is a call to {@code eval}
 271      */
 272     public boolean isEval() {
 273         return evalArgs != null;
 274     }
 275 
 276     /**
 277      * Is this an apply call that we optimistically should try to turn into
 278      * a call instead
 279      * @return true if apply to call
 280      */
 281     public boolean isApplyToCall() {
 282         return (flags & IS_APPLY_TO_CALL) != 0;
 283     }
 284 
 285     /**
 286      * Flag this call node as one that tries to call call instead of apply
 287      * @return new call node with changed flags, if not already flagged as apply to call, then the same node
 288      */
 289     public CallNode setIsApplyToCall() {
 290         return setFlags(flags | IS_APPLY_TO_CALL);
 291     }
 292 
 293     /**
 294      * Return the function expression that this call invokes
 295      * @return the function
 296      */
 297     public Expression getFunction() {
 298         return function;
 299     }
 300 
 301     /**
 302      * Reset the function expression that this call invokes
 303      * @param function the function
 304      * @return same node or new one on state change
 305      */
 306     public CallNode setFunction(final Expression function) {
 307         if (this.function == function) {
 308             return this;
 309         }
 310         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 311     }
 312 
 313     /**
 314      * Check if this call is a new operation
 315      * @return true if this a new operation
 316      */
 317     public boolean isNew() {
 318         return (flags & IS_NEW) != 0;
 319     }
 320 
 321     private CallNode setFlags(final int flags) {
 322         if (this.flags == flags) {
 323             return this;
 324         }
 325         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 326     }
 327 
 328     @Override
 329     public int getProgramPoint() {
 330         return programPoint;
 331     }
 332 
 333     @Override
 334     public CallNode setProgramPoint(final int programPoint) {
 335         if (this.programPoint == programPoint) {
 336             return this;
 337         }
 338         return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
 339     }
 340 
 341     @Override
 342     public Type getMostOptimisticType() {
 343         return Type.INT;
 344     }
 345 
 346     @Override
 347     public Type getMostPessimisticType() {
 348         return Type.OBJECT;
 349     }
 350 
 351     @Override
 352     public boolean canBeOptimistic() {
 353         return true;
 354     }
 355 }