package com.oracle.truffle.js.nodes.cast;
import java.util.Set;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
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.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.cast.JSToStringNodeGen.JSToStringWrapperNodeGen;
import com.oracle.truffle.js.nodes.unary.JSUnaryNode;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.Boundaries;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.objects.JSLazyString;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.Undefined;
public abstract class JSToStringNode extends JavaScriptBaseNode {
protected static final int MAX_CLASSES = 3;
final boolean undefinedToEmpty;
final boolean symbolToString;
@Child private JSToStringNode toStringNode;
protected JSToStringNode(boolean undefinedToEmpty, boolean symbolToString) {
this.undefinedToEmpty = undefinedToEmpty;
this.symbolToString = symbolToString;
}
public static JSToStringNode create() {
return JSToStringNodeGen.create(false, false);
}
public static JSToStringNode createUndefinedToEmpty() {
return JSToStringNodeGen.create(true, false);
}
public static JSToStringNode createSymbolToString() {
return JSToStringNodeGen.create(false, true);
}
public abstract String executeString(Object operand);
@Specialization
protected String doLazyString(JSLazyString value,
@Cached("createBinaryProfile()") ConditionProfile flattenProfile) {
return value.toString(flattenProfile);
}
@Specialization
protected String doString(String value) {
return value;
}
@Specialization(guards = "isJSNull(value)")
protected String doNull(@SuppressWarnings("unused") Object value) {
return Null.NAME;
}
@Specialization(guards = "isUndefined(value)")
protected String doUndefined(@SuppressWarnings("unused") Object value) {
return undefinedToEmpty ? "" : Undefined.NAME;
}
@Specialization
protected String doBoolean(boolean value) {
return JSRuntime.booleanToString(value);
}
@Specialization
protected String doInteger(int value) {
return Boundaries.stringValueOf(value);
}
@Specialization
protected String doBigInt(BigInt value) {
return Boundaries.stringValueOf(value);
}
@Specialization
protected String doLong(long value) {
return Boundaries.stringValueOf(value);
}
@Specialization
protected String doDouble(double d, @Cached("create()") JSDoubleToStringNode doubleToStringNode) {
return doubleToStringNode.executeString(d);
}
@Specialization(guards = "isJSDynamicObject(value)", replaces = "doUndefined")
protected String doJSObject(DynamicObject value,
@Cached("createHintString()") JSToPrimitiveNode toPrimitiveHintStringNode) {
return (undefinedToEmpty && (value == Undefined.instance)) ? "" : getToStringNode().executeString(toPrimitiveHintStringNode.execute(value));
}
@TruffleBoundary
@Specialization
protected String doSymbol(Symbol value) {
if (symbolToString) {
return value.toString();
} else {
throw Errors.createTypeErrorCannotConvertToString("a Symbol value", this);
}
}
@Specialization(guards = {"isForeignObject(object)"})
protected String doTruffleObject(Object object,
@Cached("createHintString()") JSToPrimitiveNode toPrimitiveNode) {
return getToStringNode().executeString(toPrimitiveNode.execute(object));
}
protected JSToStringNode getToStringNode() {
if (toStringNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toStringNode = insert(JSToStringNode.create());
}
return toStringNode;
}
public abstract static class JSToStringWrapperNode extends JSUnaryNode {
@Child private JSToStringNode toStringNode;
protected JSToStringWrapperNode(JavaScriptNode operand) {
super(operand);
}
public static JSToStringWrapperNode create(JavaScriptNode child) {
return JSToStringWrapperNodeGen.create(child);
}
@Override
public boolean isResultAlwaysOfType(Class<?> clazz) {
return clazz == String.class;
}
@Specialization
protected String doDefault(Object value) {
if (toStringNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toStringNode = insert(JSToStringNode.create());
}
return toStringNode.executeString(value);
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return JSToStringWrapperNodeGen.create(cloneUninitialized(getOperand(), materializedTags));
}
}
}