package com.oracle.truffle.js.nodes.cast;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.Truncatable;
import com.oracle.truffle.js.nodes.access.JSConstantNode;
import com.oracle.truffle.js.nodes.instrumentation.JSTags;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.UnaryOperationTag;
import com.oracle.truffle.js.nodes.unary.JSUnaryNode;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.SafeInteger;
import com.oracle.truffle.js.runtime.Symbol;
import java.util.Set;
public abstract class JSToInt32Node extends JSUnaryNode {
protected JSToInt32Node(JavaScriptNode operand) {
super(operand);
}
@Override
public final Object execute(VirtualFrame frame) {
return executeInt(frame);
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if (tag == UnaryOperationTag.class) {
return true;
} else {
return super.hasTag(tag);
}
}
@Override
public Object getNodeObject() {
return JSTags.createNodeObjectDescriptor("operator", getClass().getAnnotation(NodeInfo.class).shortName());
}
@Override
public abstract int executeInt(VirtualFrame frame);
public abstract int executeInt(Object operand);
public static JavaScriptNode create(JavaScriptNode child) {
if (child != null) {
if (child.isResultAlwaysOfType(int.class)) {
return child;
}
Truncatable.truncate(child);
if (child instanceof JSConstantNode) {
Object constantOperand = ((JSConstantNode) child).getValue();
if (constantOperand != null && !(constantOperand instanceof Symbol) && JSRuntime.isJSPrimitive(constantOperand)) {
return JSConstantNode.createInt(JSRuntime.toInt32(constantOperand));
}
}
}
return JSToInt32NodeGen.create(child);
}
public static JSToInt32Node create() {
return JSToInt32NodeGen.create(null);
}
@Override
public boolean isResultAlwaysOfType(Class<?> clazz) {
return clazz == int.class;
}
@Specialization
protected int doInteger(int value) {
return value;
}
@Specialization
protected int doSafeInteger(SafeInteger value) {
return value.intValue();
}
@Specialization
protected int doBoolean(boolean value) {
return JSRuntime.booleanToNumber(value);
}
@Specialization(guards = "isLongRepresentableAsInt32(value)")
protected int doLong(long value) {
return (int) value;
}
@Specialization(guards = "!isDoubleLargerThan2e32(value)")
protected int doDoubleFitsInt(double value) {
return (int) (long) value;
}
@Specialization(guards = {"isDoubleLargerThan2e32(value)", "isDoubleRepresentableAsLong(value)", "isDoubleSafeInteger(value)"})
protected int doDoubleRepresentableAsSafeInteger(double value) {
assert !Double.isFinite(value) || value % 1 == 0;
assert !Double.isNaN(value);
assert !JSRuntime.isNegativeZero(value);
return (int) (long) value;
}
@Specialization(guards = {"isDoubleLargerThan2e32(value)", "isDoubleRepresentableAsLong(value)"}, replaces = "doDoubleRepresentableAsSafeInteger")
protected int doDoubleRepresentableAsLong(double value) {
assert !Double.isFinite(value) || value % 1 == 0;
return JSRuntime.toInt32NoTruncate(value);
}
@Specialization(guards = {"isDoubleLargerThan2e32(value)", "!isDoubleRepresentableAsLong(value)"})
protected int doDouble(double value) {
return JSRuntime.toInt32(value);
}
@Specialization(guards = "isUndefined(value)")
protected int doUndefined(@SuppressWarnings("unused") Object value) {
return 0;
}
@Specialization(guards = "isJSNull(value)")
protected int doNull(@SuppressWarnings("unused") Object value) {
return 0;
}
@Specialization
protected int doString(String value,
@Cached("create()") JSStringToNumberNode stringToNumberNode) {
return doubleToInt32(stringToNumberNode.executeString(value));
}
@Specialization
protected final int doSymbol(@SuppressWarnings("unused") Symbol value) {
throw Errors.createTypeErrorCannotConvertToNumber("a Symbol value", this);
}
@Specialization
protected int doBigInt(@SuppressWarnings("unused") BigInt value) {
throw Errors.createTypeErrorCannotConvertBigIntToNumber(this);
}
@Specialization(guards = "isJSObject(value)")
protected int doJSObject(DynamicObject value,
@Cached("create()") JSToDoubleNode toDoubleNode) {
return doubleToInt32(toDoubleNode.executeDouble(value));
}
private static int doubleToInt32(double d) {
if (Double.isInfinite(d) || Double.isNaN(d) || d == 0) {
return 0;
}
return JSRuntime.toInt32(d);
}
@Specialization(guards = "isForeignObject(object)")
protected static int doForeignObject(Object object,
@Cached("createHintNumber()") JSToPrimitiveNode toPrimitiveNode,
@Cached("create()") JSToInt32Node toInt32Node) {
return toInt32Node.executeInt(toPrimitiveNode.execute(object));
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return JSToInt32NodeGen.create(cloneUninitialized(getOperand(), materializedTags));
}
}