blob: 95a116652d3704c73785af89c8d8ed5840b0553b [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
* IR representation for a function call.
*/
@Immutable
public final class CallNode extends LexicalContextExpression implements Optimistic {
private static final long serialVersionUID = 1L;
/** Function identifier or function body. */
private final Expression function;
/** Call arguments. */
private final List<Expression> args;
/** Is this a "new" operation */
private static final int IS_NEW = 1 << 0;
/** Can this be a Function.call? */
private static final int IS_APPLY_TO_CALL = 1 << 1;
private final int flags;
private final int lineNumber;
private final int programPoint;
private final Type optimisticType;
/**
* Arguments to be passed to builtin {@code eval} function
*/
public static class EvalArgs implements Serializable {
private static final long serialVersionUID = 1L;
private final List<Expression> args;
/** location string for the eval call */
private final String location;
/**
* Constructor
*
* @param args arguments to eval
* @param location location for the eval call
*/
public EvalArgs(final List<Expression> args, final String location) {
this.args = args;
this.location = location;
}
/**
* Return the code that is to be eval:ed by this eval function
* @return code as an AST node
*/
public List<Expression> getArgs() {
return Collections.unmodifiableList(args);
}
private EvalArgs setArgs(final List<Expression> args) {
if (this.args == args) {
return this;
}
return new EvalArgs(args, location);
}
/**
* Get the human readable location for this eval call
* @return the location
*/
public String getLocation() {
return this.location;
}
}
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@Ignore
private final EvalArgs evalArgs;
/**
* Constructors
*
* @param lineNumber line number
* @param token token
* @param finish finish
* @param function the function to call
* @param args args to the call
* @param isNew true if this is a constructor call with the "new" keyword
*/
public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args, final boolean isNew) {
super(token, finish);
this.function = function;
this.args = args;
this.flags = isNew ? IS_NEW : 0;
this.evalArgs = null;
this.lineNumber = lineNumber;
this.programPoint = INVALID_PROGRAM_POINT;
this.optimisticType = null;
}
private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) {
super(callNode);
this.lineNumber = callNode.lineNumber;
this.function = function;
this.args = args;
this.flags = flags;
this.evalArgs = evalArgs;
this.programPoint = programPoint;
this.optimisticType = optimisticType;
}
/**
* Returns the line number.
* @return the line number.
*/
public int getLineNumber() {
return lineNumber;
}
@Override
public Type getType() {
return optimisticType == null ? Type.OBJECT : optimisticType;
}
@Override
public Optimistic setType(final Type optimisticType) {
if (this.optimisticType == optimisticType) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
* Assist in IR navigation.
*
* @param visitor IR navigating visitor.
*
* @return node or replacement
*/
@Override
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterCallNode(this)) {
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
setFunction((Expression)function.accept(visitor)).
setArgs(Node.accept(visitor, args)).
setEvalArgs(evalArgs == null ?
null :
evalArgs.setArgs(Node.accept(visitor, evalArgs.getArgs()))));
// Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
// setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
if (this != newCallNode) {
return Node.replaceInLexicalContext(lc, this, newCallNode);
}
}
return this;
}
@Override
public void toString(final StringBuilder sb, final boolean printType) {
if (printType) {
optimisticTypeToString(sb);
}
final StringBuilder fsb = new StringBuilder();
function.toString(fsb, printType);
if (isApplyToCall()) {
sb.append(fsb.toString().replace("apply", "[apply => call]"));
} else {
sb.append(fsb);
}
sb.append('(');
boolean first = true;
for (final Node arg : args) {
if (!first) {
sb.append(", ");
} else {
first = false;
}
arg.toString(sb, printType);
}
sb.append(')');
}
/**
* Get the arguments for the call
* @return a list of arguments
*/
public List<Expression> getArgs() {
return Collections.unmodifiableList(args);
}
/**
* Reset the arguments for the call
* @param args new arguments list
* @return new callnode, or same if unchanged
*/
public CallNode setArgs(final List<Expression> args) {
if (this.args == args) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
* If this call is an {@code eval} call, get its EvalArgs structure
* @return EvalArgs for call
*/
public EvalArgs getEvalArgs() {
return evalArgs;
}
/**
* Set the EvalArgs structure for this call, if it has been determined it is an
* {@code eval}
*
* @param evalArgs eval args
* @return same node or new one on state change
*/
public CallNode setEvalArgs(final EvalArgs evalArgs) {
if (this.evalArgs == evalArgs) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
* Check if this call is a call to {@code eval}
* @return true if this is a call to {@code eval}
*/
public boolean isEval() {
return evalArgs != null;
}
/**
* Is this an apply call that we optimistically should try to turn into
* a call instead
* @return true if apply to call
*/
public boolean isApplyToCall() {
return (flags & IS_APPLY_TO_CALL) != 0;
}
/**
* Flag this call node as one that tries to call call instead of apply
* @return new call node with changed flags, if not already flagged as apply to call, then the same node
*/
public CallNode setIsApplyToCall() {
return setFlags(flags | IS_APPLY_TO_CALL);
}
/**
* Return the function expression that this call invokes
* @return the function
*/
public Expression getFunction() {
return function;
}
/**
* Reset the function expression that this call invokes
* @param function the function
* @return same node or new one on state change
*/
public CallNode setFunction(final Expression function) {
if (this.function == function) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
* Check if this call is a new operation
* @return true if this a new operation
*/
public boolean isNew() {
return (flags & IS_NEW) != 0;
}
private CallNode setFlags(final int flags) {
if (this.flags == flags) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
@Override
public int getProgramPoint() {
return programPoint;
}
@Override
public CallNode setProgramPoint(final int programPoint) {
if (this.programPoint == programPoint) {
return this;
}
return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
@Override
public Type getMostOptimisticType() {
return Type.INT;
}
@Override
public Type getMostPessimisticType() {
return Type.OBJECT;
}
@Override
public boolean canBeOptimistic() {
return true;
}
}