package com.oracle.truffle.js.nodes.cast;
import com.oracle.truffle.api.dsl.ImportStatic;
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.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.access.JSConstantNode;
import com.oracle.truffle.js.nodes.access.JSConstantNode.JSConstantBigIntNode;
import com.oracle.truffle.js.nodes.access.JSConstantNode.JSConstantIntegerNode;
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.JSConfig;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.objects.JSLazyString;
import java.util.Set;
@ImportStatic({JSRuntime.class, JSConfig.class})
public abstract class JSToBooleanNode extends JSUnaryNode {
protected JSToBooleanNode(JavaScriptNode operand) {
super(operand);
}
@Override
public final Object execute(VirtualFrame frame) {
return executeBoolean(frame);
}
@Override
public abstract boolean executeBoolean(VirtualFrame frame);
public abstract boolean executeBoolean(Object value);
@Override
public boolean isResultAlwaysOfType(Class<?> clazz) {
return clazz == boolean.class;
}
public static JSToBooleanNode create() {
return JSToBooleanNodeGen.create(null);
}
public static JavaScriptNode create(JavaScriptNode child) {
JSConstantNode replacement = null;
if (child.isResultAlwaysOfType(boolean.class)) {
return child;
} else if (child instanceof JSConstantIntegerNode) {
int value = ((JSConstantIntegerNode) child).executeInt(null);
replacement = JSConstantNode.createBoolean(value != 0);
} else if (child instanceof JSConstantBigIntNode) {
BigInt value = ((JSConstantBigIntNode) child).executeBigInt(null);
replacement = JSConstantNode.createBoolean(value.compareTo(BigInt.ZERO) != 0);
} else if (child instanceof JSConstantNode) {
Object constantOperand = ((JSConstantNode) child).getValue();
if (constantOperand != null && JSRuntime.isJSPrimitive(constantOperand)) {
replacement = JSConstantNode.createBoolean(JSRuntime.toBoolean(constantOperand));
}
}
if (replacement == null) {
return JSToBooleanNodeGen.create(child);
} else {
transferSourceSectionAndTags(child, replacement);
return replacement;
}
}
@Specialization
protected boolean doBoolean(boolean value) {
return value;
}
@Specialization(guards = "isJSNull(value)")
protected boolean doNull(@SuppressWarnings("unused") Object value) {
return false;
}
@Specialization(guards = "isUndefined(value)")
protected boolean doUndefined(@SuppressWarnings("unused") Object value) {
return false;
}
@Specialization
protected boolean doInt(int value) {
return value != 0;
}
@Specialization
protected boolean doLong(long value) {
return value != 0L;
}
@Specialization
protected boolean doDouble(double value) {
return value != 0.0 && !Double.isNaN(value);
}
@Specialization
protected boolean doBigInt(BigInt value) {
return value.compareTo(BigInt.ZERO) != 0;
}
@Specialization
protected boolean doLazyString(JSLazyString value) {
return !value.isEmpty();
}
@Specialization
protected boolean doString(String value) {
return value.length() > 0;
}
@Specialization(guards = "isJSObject(value)")
protected boolean doObject(@SuppressWarnings("unused") DynamicObject value) {
return true;
}
@Specialization
protected boolean doSymbol(@SuppressWarnings("unused") Symbol value) {
return true;
}
@Specialization(guards = "isForeignObject(value)", limit = "InteropLibraryLimit")
protected boolean doForeignObject(Object value,
@CachedLibrary("value") InteropLibrary interop) {
if (interop.isNull(value)) {
return false;
}
try {
if (interop.isBoolean(value)) {
return interop.asBoolean(value);
} else if (interop.isString(value)) {
return !interop.asString(value).isEmpty();
} else if (interop.isNumber(value)) {
if (interop.fitsInInt(value)) {
return doInt(interop.asInt(value));
} else if (interop.fitsInLong(value)) {
return doLong(interop.asLong(value));
} else if (interop.fitsInDouble(value)) {
return doDouble(interop.asDouble(value));
} else {
return true;
}
}
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(value, e, this);
}
return true;
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return JSToBooleanNodeGen.create(cloneUninitialized(getOperand(), materializedTags));
}
}