aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/ir/CallNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/ir/CallNode.java')
-rw-r--r--src/jdk/nashorn/internal/ir/CallNode.java195
1 files changed, 117 insertions, 78 deletions
diff --git a/src/jdk/nashorn/internal/ir/CallNode.java b/src/jdk/nashorn/internal/ir/CallNode.java
index 0d38f241..72eab68b 100644
--- a/src/jdk/nashorn/internal/ir/CallNode.java
+++ b/src/jdk/nashorn/internal/ir/CallNode.java
@@ -25,8 +25,12 @@
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 java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
@@ -36,7 +40,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* IR representation for a function call.
*/
@Immutable
-public final class CallNode extends LexicalContextExpression {
+public final class CallNode extends LexicalContextExpression implements Optimistic {
+ private static final long serialVersionUID = 1L;
/** Function identifier or function body. */
private final Expression function;
@@ -45,71 +50,53 @@ public final class CallNode extends LexicalContextExpression {
private final List<Expression> args;
/** Is this a "new" operation */
- public static final int IS_NEW = 0x1;
+ 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 {
- /** evaluated code */
- private final Expression code;
-
- /** 'this' passed to evaluated code */
- private final IdentNode evalThis;
+ 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;
- /** is this call from a strict context? */
- private final boolean strictMode;
-
/**
* Constructor
*
- * @param code code to evaluate
- * @param evalThis this node
- * @param location location for the eval call
- * @param strictMode is this a call from a strict context?
+ * @param args arguments to eval
+ * @param location location for the eval call
*/
- public EvalArgs(final Expression code, final IdentNode evalThis, final String location, final boolean strictMode) {
- this.code = code;
- this.evalThis = evalThis;
+ public EvalArgs(final List<Expression> args, final String location) {
+ this.args = args;
this.location = location;
- this.strictMode = strictMode;
}
/**
* Return the code that is to be eval:ed by this eval function
* @return code as an AST node
*/
- public Expression getCode() {
- return code;
+ public List<Expression> getArgs() {
+ return Collections.unmodifiableList(args);
}
- private EvalArgs setCode(final Expression code) {
- if (this.code == code) {
+ private EvalArgs setArgs(final List<Expression> args) {
+ if (this.args == args) {
return this;
}
- return new EvalArgs(code, evalThis, location, strictMode);
- }
-
- /**
- * Get the {@code this} symbol used to invoke this eval call
- * @return the {@code this} symbol
- */
- public IdentNode getThis() {
- return this.evalThis;
- }
-
- private EvalArgs setThis(final IdentNode evalThis) {
- if (this.evalThis == evalThis) {
- return this;
- }
- return new EvalArgs(code, evalThis, location, strictMode);
+ return new EvalArgs(args, location);
}
/**
@@ -119,14 +106,6 @@ public final class CallNode extends LexicalContextExpression {
public String getLocation() {
return this.location;
}
-
- /**
- * Check whether this eval call is executed in strict mode
- * @return true if executed in strict mode, false otherwise
- */
- public boolean getStrictMode() {
- return this.strictMode;
- }
}
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@@ -141,24 +120,29 @@ public final class CallNode extends LexicalContextExpression {
* @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) {
+ 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 = 0;
- this.evalArgs = null;
- this.lineNumber = lineNumber;
+ 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 EvalArgs evalArgs) {
+ 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;
}
/**
@@ -170,8 +154,16 @@ public final class CallNode extends LexicalContextExpression {
}
@Override
- public Type getType() {
- return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
+ 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);
}
/**
@@ -186,15 +178,13 @@ public final class CallNode extends LexicalContextExpression {
if (visitor.enterCallNode(this)) {
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
setFunction((Expression)function.accept(visitor)).
- setArgs(Node.accept(visitor, Expression.class, args)).
- setFlags(flags).
+ setArgs(Node.accept(visitor, args)).
setEvalArgs(evalArgs == null ?
null :
- evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
- setThis((IdentNode)evalArgs.getThis().accept(visitor))));
+ 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) {
+ if (this != newCallNode) {
return Node.replaceInLexicalContext(lc, this, newCallNode);
}
}
@@ -203,8 +193,19 @@ public final class CallNode extends LexicalContextExpression {
}
@Override
- public void toString(final StringBuilder sb) {
- function.toString(sb);
+ 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('(');
@@ -217,7 +218,7 @@ public final class CallNode extends LexicalContextExpression {
first = false;
}
- arg.toString(sb);
+ arg.toString(sb, printType);
}
sb.append(')');
@@ -234,12 +235,13 @@ public final class CallNode extends LexicalContextExpression {
/**
* Reset the arguments for the call
* @param args new arguments list
+ * @return new callnode, or same if unchanged
*/
- private CallNode setArgs(final List<Expression> args) {
+ public CallNode setArgs(final List<Expression> args) {
if (this.args == args) {
return this;
}
- return new CallNode(this, function, args, flags, evalArgs);
+ return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
@@ -261,7 +263,7 @@ public final class CallNode extends LexicalContextExpression {
if (this.evalArgs == evalArgs) {
return this;
}
- return new CallNode(this, function, args, flags, evalArgs);
+ return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
@@ -273,6 +275,23 @@ public final class CallNode extends LexicalContextExpression {
}
/**
+ * 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
*/
@@ -289,7 +308,7 @@ public final class CallNode extends LexicalContextExpression {
if (this.function == function) {
return this;
}
- return new CallNode(this, function, args, flags, evalArgs);
+ return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
}
/**
@@ -297,21 +316,41 @@ public final class CallNode extends LexicalContextExpression {
* @return true if this a new operation
*/
public boolean isNew() {
- return (flags & IS_NEW) == IS_NEW;
- }
-
- /**
- * Flag this call as a new operation
- * @return same node or new one on state change
- */
- public CallNode setIsNew() {
- return setFlags(IS_NEW);
+ return (flags & IS_NEW) != 0;
}
private CallNode setFlags(final int flags) {
if (this.flags == flags) {
return this;
}
- return new CallNode(this, function, args, flags, evalArgs);
+ 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;
}
}