package com.oracle.truffle.js.nodes.binary;
import java.util.Objects;
import java.util.Set;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.access.JSConstantNode;
import com.oracle.truffle.js.nodes.cast.JSToInt32Node;
import com.oracle.truffle.js.nodes.cast.JSToNumericNode;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.BinaryOperationTag;
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;
@NodeInfo(shortName = "&")
public abstract class JSBitwiseAndConstantNode extends JSUnaryNode {
protected final boolean isInt;
protected final int rightIntValue;
protected final BigInt rightBigIntValue;
protected JSBitwiseAndConstantNode(JavaScriptNode left, Object rightValue) {
super(left);
if (rightValue instanceof BigInt) {
this.isInt = false;
this.rightIntValue = 0;
this.rightBigIntValue = (BigInt) rightValue;
} else {
this.isInt = true;
this.rightIntValue = (int) rightValue;
this.rightBigIntValue = null;
}
}
public static JSBitwiseAndConstantNode create(JavaScriptNode left, Object right) {
return JSBitwiseAndConstantNodeGen.create(left, right);
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if (tag == BinaryOperationTag.class) {
return true;
} else {
return super.hasTag(tag);
}
}
@Override
public InstrumentableNode materializeInstrumentableNodes(Set<Class<? extends Tag>> materializedTags) {
if (materializedTags.contains(BinaryOperationTag.class)) {
JSConstantNode constantNode = JSConstantNode.create(isInt ? rightIntValue : rightBigIntValue);
JavaScriptNode node = JSBitwiseAndNodeGen.create(cloneUninitialized(getOperand(), materializedTags), constantNode);
transferSourceSectionAddExpressionTag(this, constantNode);
transferSourceSectionAndTags(this, node);
return node;
} else {
return this;
}
}
public abstract Object executeObject(Object a);
@Specialization(guards = "isInt")
protected int doInteger(int a) {
return a & rightIntValue;
}
@Specialization(guards = "isInt")
protected int doSafeInteger(SafeInteger a) {
return doInteger(a.intValue());
}
@Specialization(guards = "isInt")
protected int doDouble(double a, @Cached("create()") JSToInt32Node leftInt32) {
return doInteger(leftInt32.executeInt(a));
}
@Specialization(guards = "!isInt")
protected void doIntegerThrows(@SuppressWarnings("unused") int a) {
throw Errors.createTypeErrorCannotMixBigIntWithOtherTypes(this);
}
@Specialization(guards = "!isInt")
protected void doDoubleThrows(@SuppressWarnings("unused") double a) {
throw Errors.createTypeErrorCannotMixBigIntWithOtherTypes(this);
}
@Specialization(guards = "isInt")
protected void doBigIntThrows(@SuppressWarnings("unused") BigInt a) {
throw Errors.createTypeErrorCannotMixBigIntWithOtherTypes(this);
}
@Specialization(guards = "!isInt")
protected BigInt doBigInt(BigInt a) {
return a.and(rightBigIntValue);
}
@Specialization(replaces = {"doInteger", "doSafeInteger", "doDouble", "doBigIntThrows"}, guards = "isInt")
protected Object doGeneric(Object a,
@Cached("create()") JSToNumericNode toNumeric,
@Cached("createBinaryProfile()") ConditionProfile profileIsBigInt,
@Cached("makeCopy()") JavaScriptNode innerAndNode) {
Object numericA = toNumeric.execute(a);
if (profileIsBigInt.profile(JSRuntime.isBigInt(numericA))) {
throw Errors.createTypeErrorCannotMixBigIntWithOtherTypes(this);
} else {
return ((JSBitwiseAndConstantNode) innerAndNode).executeObject(numericA);
}
}
protected JSBitwiseAndConstantNode makeCopy() {
return (JSBitwiseAndConstantNode) copyUninitialized(null);
}
protected final boolean isInt() {
return isInt;
}
@Specialization(replaces = {"doIntegerThrows", "doDoubleThrows", "doBigInt"}, guards = "!isInt()")
protected BigInt doGenericBigIntCase(Object a,
@Cached("create()") JSToNumericNode toNumeric,
@Cached("createBinaryProfile()") ConditionProfile profileIsBigInt) {
Object numericA = toNumeric.execute(a);
if (profileIsBigInt.profile(JSRuntime.isBigInt(numericA))) {
return doBigInt((BigInt) numericA);
} else {
throw Errors.createTypeErrorCannotMixBigIntWithOtherTypes(this);
}
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return JSBitwiseAndConstantNodeGen.create(cloneUninitialized(getOperand(), materializedTags), isInt ? rightIntValue : rightBigIntValue);
}
@Override
public String expressionToString() {
if (getOperand() != null) {
return "(" + Objects.toString(getOperand().expressionToString(), INTERMEDIATE_VALUE) + " & " + (isInt ? rightIntValue : rightBigIntValue.toString()) + ")";
}
return null;
}
}