package com.oracle.truffle.js.nodes.binary;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.Truncatable;
import com.oracle.truffle.js.nodes.access.JSConstantNode.JSConstantIntegerNode;
import com.oracle.truffle.js.nodes.cast.JSToNumericNode;
import com.oracle.truffle.js.nodes.cast.JSToUInt32Node;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.SafeInteger;
import java.util.Set;
@NodeInfo(shortName = ">>>")
public abstract class JSUnsignedRightShiftNode extends JSBinaryNode {
protected JSUnsignedRightShiftNode(JavaScriptNode left, JavaScriptNode right) {
super(left, right);
}
@Child private JSToUInt32Node toUInt32Node;
public static JavaScriptNode create(JavaScriptNode left, JavaScriptNode right) {
Truncatable.truncate(left);
Truncatable.truncate(right);
if (JSConfig.UseSuperOperations && right instanceof JSConstantIntegerNode) {
return JSUnsignedRightShiftConstantNode.create(left, right);
}
return JSUnsignedRightShiftNodeGen.create(left, right);
}
static JSUnsignedRightShiftNode create() {
return JSUnsignedRightShiftNodeGen.create(null, null);
}
protected final Number executeNumber(Object a, Object b) {
return (Number) execute(a, b);
}
public abstract Object execute(Object a, Object b);
protected static boolean rvalZero(int b) {
return (b & 0x1f) == 0;
}
@Specialization(guards = {"rvalZero(b)", "a >= 0"})
protected int doIntegerFast(int a, @SuppressWarnings("unused") int b) {
return a;
}
@Specialization(guards = "a >= 0")
protected int doInteger(int a, int b) {
return a >>> b;
}
@Specialization(guards = "!rvalZero(b)")
protected int doIntegerNegative(int a, int b) {
return a >>> b;
}
@Specialization(guards = "rvalZero(b)")
protected double doDoubleZero(double a, @SuppressWarnings("unused") int b) {
return toUInt32(a);
}
@Specialization(guards = "!rvalZero(b)")
protected Number doDouble(double a, int b,
@Cached("createBinaryProfile()") ConditionProfile returnType) {
long lnum = toUInt32(a);
int shiftCount = b & 0x1F;
if (returnType.profile(lnum >= Integer.MAX_VALUE || lnum <= Integer.MIN_VALUE)) {
return (double) (lnum >>> shiftCount);
}
return (int) (lnum >>> shiftCount);
}
@Specialization
protected Number doIntDouble(int a, double b,
@Cached("create()") JSToUInt32Node rvalToUint32Node,
@Cached("createBinaryProfile()") ConditionProfile returnType) {
long lnum = toUInt32(a);
int shiftCount = (int) rvalToUint32Node.executeLong(b) & 0x1F;
if (returnType.profile(lnum >= Integer.MAX_VALUE || lnum <= Integer.MIN_VALUE)) {
return (double) (lnum >>> shiftCount);
}
return (int) (lnum >>> shiftCount);
}
@Specialization
protected double doDoubleDouble(double a, double b) {
return (toUInt32(a) >>> ((int) toUInt32(b) & 0x1F));
}
@Specialization
protected Number doBigInt(@SuppressWarnings("unused") BigInt a, @SuppressWarnings("unused") BigInt b) {
throw Errors.createTypeError("BigInts have no unsigned right shift, use >> instead");
}
@Specialization(guards = "!isHandled(lval, rval)")
protected Number doGeneric(Object lval, Object rval,
@Cached("create()") JSToNumericNode lvalToNumericNode,
@Cached("create()") JSToNumericNode rvalToNumericNode,
@Cached("create()") JSUnsignedRightShiftNode innerShiftNode,
@Cached("create()") BranchProfile mixedNumericTypes) {
Object lnum = lvalToNumericNode.execute(lval);
Object rnum = rvalToNumericNode.execute(rval);
ensureBothSameNumericType(lnum, rnum, mixedNumericTypes);
return innerShiftNode.executeNumber(lnum, rnum);
}
private long toUInt32(Object target) {
if (toUInt32Node == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toUInt32Node = insert(JSToUInt32Node.create());
}
return toUInt32Node.executeLong(target);
}
protected static boolean isHandled(Object lval, Object rval) {
return (lval instanceof Integer || lval instanceof Double || lval instanceof SafeInteger) && (rval instanceof Integer || rval instanceof Double || rval instanceof SafeInteger);
}
@Override
public boolean isResultAlwaysOfType(Class<?> clazz) {
return clazz == Number.class;
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return JSUnsignedRightShiftNodeGen.create(cloneUninitialized(getLeft(), materializedTags), cloneUninitialized(getRight(), materializedTags));
}
}