/*
* Copyright (c) 2010, 2015, 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 java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
IR representation for a runtime call.
/**
* IR representation for a runtime call.
*/
@Immutable
public class RuntimeNode extends Expression {
private static final long serialVersionUID = 1L;
Request enum used for meta-information about the runtime request
/**
* Request enum used for meta-information about the runtime request
*/
public enum Request {
An addition with at least one object /** An addition with at least one object */
ADD(TokenType.ADD, Type.OBJECT, 2, true),
Request to enter debugger /** Request to enter debugger */
DEBUGGER,
New operator /** New operator */
NEW,
Typeof operator /** Typeof operator */
TYPEOF,
Reference error type /** Reference error type */
REFERENCE_ERROR,
Delete operator /** Delete operator */
DELETE(TokenType.DELETE, Type.BOOLEAN, 1),
Delete operator for slow scopes /** Delete operator for slow scopes */
SLOW_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false),
Delete operator that always fails -- see Lower /** Delete operator that always fails -- see Lower */
FAIL_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false),
=== operator with at least one object /** === operator with at least one object */
EQ_STRICT(TokenType.EQ_STRICT, Type.BOOLEAN, 2, true),
== operator with at least one object /** == operator with at least one object */
EQ(TokenType.EQ, Type.BOOLEAN, 2, true),
>= operator with at least one object /** {@literal >=} operator with at least one object */
GE(TokenType.GE, Type.BOOLEAN, 2, true),
> operator with at least one object /** {@literal >} operator with at least one object */
GT(TokenType.GT, Type.BOOLEAN, 2, true),
in operator /** in operator */
IN(TokenType.IN, Type.BOOLEAN, 2),
instanceof operator /** instanceof operator */
INSTANCEOF(TokenType.INSTANCEOF, Type.BOOLEAN, 2),
<= operator with at least one object /** {@literal <=} operator with at least one object */
LE(TokenType.LE, Type.BOOLEAN, 2, true),
< operator with at least one object /** {@literal <} operator with at least one object */
LT(TokenType.LT, Type.BOOLEAN, 2, true),
!== operator with at least one object /** !== operator with at least one object */
NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
!= operator with at least one object /** != operator with at least one object */
NE(TokenType.NE, Type.BOOLEAN, 2, true),
is undefined /** is undefined */
IS_UNDEFINED(TokenType.EQ_STRICT, Type.BOOLEAN, 2),
is not undefined /** is not undefined */
IS_NOT_UNDEFINED(TokenType.NE_STRICT, Type.BOOLEAN, 2),
Get template object from raw and cooked string arrays. /** Get template object from raw and cooked string arrays. */
GET_TEMPLATE_OBJECT(TokenType.TEMPLATE, Type.SCRIPT_OBJECT, 2);
token type /** token type */
private final TokenType tokenType;
return type for request /** return type for request */
private final Type returnType;
arity of request /** arity of request */
private final int arity;
Can the specializer turn this into something that works with 1 or more primitives? /** Can the specializer turn this into something that works with 1 or more primitives? */
private final boolean canSpecialize;
private Request() {
this(TokenType.VOID, Type.OBJECT, 0);
}
private Request(final TokenType tokenType, final Type returnType, final int arity) {
this(tokenType, returnType, arity, false);
}
private Request(final TokenType tokenType, final Type returnType, final int arity, final boolean canSpecialize) {
this.tokenType = tokenType;
this.returnType = returnType;
this.arity = arity;
this.canSpecialize = canSpecialize;
}
Can this request type be specialized?
Returns: true if request can be specialized
/**
* Can this request type be specialized?
*
* @return true if request can be specialized
*/
public boolean canSpecialize() {
return canSpecialize;
}
Get arity
Returns: the arity of the request
/**
* Get arity
*
* @return the arity of the request
*/
public int getArity() {
return arity;
}
Get the return type
Returns: return type for request
/**
* Get the return type
*
* @return return type for request
*/
public Type getReturnType() {
return returnType;
}
Get token type
Returns: token type for request
/**
* Get token type
*
* @return token type for request
*/
public TokenType getTokenType() {
return tokenType;
}
Get the non-strict name for this request.
Returns: the name without _STRICT suffix
/**
* Get the non-strict name for this request.
*
* @return the name without _STRICT suffix
*/
public String nonStrictName() {
switch(this) {
case NE_STRICT:
return NE.name();
case EQ_STRICT:
return EQ.name();
default:
return name();
}
}
Derive a runtime node request type for a node
Params: - node – the node
Returns: request type
/**
* Derive a runtime node request type for a node
* @param node the node
* @return request type
*/
public static Request requestFor(final Expression node) {
switch (node.tokenType()) {
case TYPEOF:
return Request.TYPEOF;
case IN:
return Request.IN;
case INSTANCEOF:
return Request.INSTANCEOF;
case EQ_STRICT:
return Request.EQ_STRICT;
case NE_STRICT:
return Request.NE_STRICT;
case EQ:
return Request.EQ;
case NE:
return Request.NE;
case LT:
return Request.LT;
case LE:
return Request.LE;
case GT:
return Request.GT;
case GE:
return Request.GE;
default:
assert false;
return null;
}
}
Is this an undefined check?
Params: - request – request
Returns: true if undefined check
/**
* Is this an undefined check?
*
* @param request request
*
* @return true if undefined check
*/
public static boolean isUndefinedCheck(final Request request) {
return request == IS_UNDEFINED || request == IS_NOT_UNDEFINED;
}
Is this an EQ or EQ_STRICT?
Params: - request – a request
Returns: true if EQ or EQ_STRICT
/**
* Is this an EQ or EQ_STRICT?
*
* @param request a request
*
* @return true if EQ or EQ_STRICT
*/
public static boolean isEQ(final Request request) {
return request == EQ || request == EQ_STRICT;
}
Is this an NE or NE_STRICT?
Params: - request – a request
Returns: true if NE or NE_STRICT
/**
* Is this an NE or NE_STRICT?
*
* @param request a request
*
* @return true if NE or NE_STRICT
*/
public static boolean isNE(final Request request) {
return request == NE || request == NE_STRICT;
}
Is this strict?
Params: - request – a request
Returns: true if script
/**
* Is this strict?
*
* @param request a request
*
* @return true if script
*/
public static boolean isStrict(final Request request) {
return request == EQ_STRICT || request == NE_STRICT;
}
If this request can be reversed, return the reverse request Eq EQ -> NE. Params: - request – request to reverse
Returns: reversed request or null if not applicable
/**
* If this request can be reversed, return the reverse request
* Eq EQ {@literal ->} NE.
*
* @param request request to reverse
*
* @return reversed request or null if not applicable
*/
public static Request reverse(final Request request) {
switch (request) {
case EQ:
case EQ_STRICT:
case NE:
case NE_STRICT:
return request;
case LE:
return GE;
case LT:
return GT;
case GE:
return LE;
case GT:
return LT;
default:
return null;
}
}
Invert the request, only for non equals comparisons.
Params: - request – a request
Returns: the inverted request, or null if not applicable
/**
* Invert the request, only for non equals comparisons.
*
* @param request a request
*
* @return the inverted request, or null if not applicable
*/
public static Request invert(final Request request) {
switch (request) {
case EQ:
return NE;
case EQ_STRICT:
return NE_STRICT;
case NE:
return EQ;
case NE_STRICT:
return EQ_STRICT;
case LE:
return GT;
case LT:
return GE;
case GE:
return LT;
case GT:
return LE;
default:
return null;
}
}
Check if this is a comparison
Params: - request – a request
Returns: true if this is a comparison, null otherwise
/**
* Check if this is a comparison
*
* @param request a request
*
* @return true if this is a comparison, null otherwise
*/
public static boolean isComparison(final Request request) {
switch (request) {
case EQ:
case EQ_STRICT:
case NE:
case NE_STRICT:
case LE:
case LT:
case GE:
case GT:
case IS_UNDEFINED:
case IS_NOT_UNDEFINED:
return true;
default:
return false;
}
}
}
Runtime request. /** Runtime request. */
private final Request request;
Call arguments. /** Call arguments. */
private final List<Expression> args;
Constructor
Params: - token – token
- finish – finish
- request – the request
- args – arguments to request
/**
* Constructor
*
* @param token token
* @param finish finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final long token, final int finish, final Request request, final List<Expression> args) {
super(token, finish);
this.request = request;
this.args = args;
}
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final List<Expression> args) {
super(runtimeNode);
this.request = request;
this.args = args;
}
Constructor
Params: - token – token
- finish – finish
- request – the request
- args – arguments to request
/**
* Constructor
*
* @param token token
* @param finish finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final long token, final int finish, final Request request, final Expression... args) {
this(token, finish, request, Arrays.asList(args));
}
Constructor
Params: - parent – parent node from which to inherit source, token, finish
- request – the request
- args – arguments to request
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final Expression parent, final Request request, final Expression... args) {
this(parent, request, Arrays.asList(args));
}
Constructor
Params: - parent – parent node from which to inherit source, token, finish
- request – the request
- args – arguments to request
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final Expression parent, final Request request, final List<Expression> args) {
super(parent);
this.request = request;
this.args = args;
}
Constructor
Params: - parent – parent node from which to inherit source, token, finish and arguments
- request – the request
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish and arguments
* @param request the request
*/
public RuntimeNode(final UnaryNode parent, final Request request) {
this(parent, request, parent.getExpression());
}
Constructor used to replace a binary node with a runtime request.
Params: - parent – parent node from which to inherit source, token, finish and arguments
/**
* Constructor used to replace a binary node with a runtime request.
*
* @param parent parent node from which to inherit source, token, finish and arguments
*/
public RuntimeNode(final BinaryNode parent) {
this(parent, Request.requestFor(parent), parent.lhs(), parent.rhs());
}
Reset the request for this runtime node
Params: - request – request
Returns: new runtime node or same if same request
/**
* Reset the request for this runtime node
* @param request request
* @return new runtime node or same if same request
*/
public RuntimeNode setRequest(final Request request) {
if (this.request == request) {
return this;
}
return new RuntimeNode(this, request, args);
}
Return type for the ReferenceNode
/**
* Return type for the ReferenceNode
*/
@Override
public Type getType() {
return request.getReturnType();
}
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterRuntimeNode(this)) {
return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args)));
}
return this;
}
@Override
public void toString(final StringBuilder sb, final boolean printType) {
sb.append("ScriptRuntime.");
sb.append(request);
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 this runtime node
Returns: argument list
/**
* Get the arguments for this runtime node
* @return argument list
*/
public List<Expression> getArgs() {
return Collections.unmodifiableList(args);
}
Set the arguments of this runtime node
Params: - args – new arguments
Returns: new runtime node, or identical if no change
/**
* Set the arguments of this runtime node
* @param args new arguments
* @return new runtime node, or identical if no change
*/
public RuntimeNode setArgs(final List<Expression> args) {
if (this.args == args) {
return this;
}
return new RuntimeNode(this, request, args);
}
Get the request that this runtime node implements
Returns: the request
/**
* Get the request that this runtime node implements
* @return the request
*/
public Request getRequest() {
return request;
}
Is this runtime node, engineered to handle the "at least one object" case of the defined
requests and specialize on demand, really primitive. This can happen e.g. after AccessSpecializer
In that case it can be turned into a simpler primitive form in CodeGenerator
Returns: true if all arguments now are primitive
/**
* Is this runtime node, engineered to handle the "at least one object" case of the defined
* requests and specialize on demand, really primitive. This can happen e.g. after AccessSpecializer
* In that case it can be turned into a simpler primitive form in CodeGenerator
*
* @return true if all arguments now are primitive
*/
public boolean isPrimitive() {
for (final Expression arg : args) {
if (arg.getType().isObject()) {
return false;
}
}
return true;
}
}