package com.oracle.truffle.api.test.interop;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.ExceptionType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.graalvm.polyglot.Context;
import org.junit.Test;
import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.test.polyglot.ProxyLanguage;
@SuppressWarnings("deprecation")
public class InteropDefaultsTest extends InteropLibraryBaseTest {
public static class TestInterop1 {
}
@Test
public void testBooleanDefault() throws InteropException {
assertBoolean(true, true);
assertBoolean(false, false);
}
private void assertBoolean(Object v, boolean expected) throws UnsupportedMessageException, InteropException {
InteropLibrary library = createLibrary(InteropLibrary.class, v);
assertTrue(library.isBoolean(v));
assertEquals(expected, library.asBoolean(v));
assertEquals(v.toString(), library.toDisplayString(v));
assertNotNull(v);
assertNoObject(v);
assertNoArray(v);
assertNoString(v);
assertNoNumber(v);
assertNoNative(v);
assertNotExecutable(v);
assertNotInstantiable(v);
assertNoMetaObject(v);
assertHasNoMetaObject(v);
assertNoDate(v);
assertNoTime(v);
assertNoTimeZone(v);
assertNoDuration(v);
assertNoSourceLocation(v);
assertNoLanguage(v);
assertNoIdentity(v);
}
@Test
public void testByteDefault() throws InteropException {
assertNumber(Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber((byte) 0, true, true, true, true, true, true);
assertNumber(Byte.MAX_VALUE, true, true, true, true, true, true);
}
@Test
public void testShortDefault() throws InteropException {
assertNumber(Short.MIN_VALUE, false, true, true, true, true, true);
assertNumber((short) (Byte.MIN_VALUE - 1), false, true, true, true, true, true);
assertNumber((short) Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber((short) 0, true, true, true, true, true, true);
assertNumber((short) Byte.MAX_VALUE, true, true, true, true, true, true);
assertNumber((short) (Byte.MAX_VALUE + 1), false, true, true, true, true, true);
assertNumber(Short.MAX_VALUE, false, true, true, true, true, true);
}
@Test
public void testIntDefault() throws InteropException {
assertNumber(Integer.MIN_VALUE, false, false, true, true, true, true);
assertNumber(Short.MIN_VALUE - 1, false, false, true, true, true, true);
assertNumber((int) Short.MIN_VALUE, false, true, true, true, true, true);
assertNumber(Byte.MIN_VALUE - 1, false, true, true, true, true, true);
assertNumber((int) Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber(0, true, true, true, true, true, true);
assertNumber((int) Byte.MAX_VALUE, true, true, true, true, true, true);
assertNumber(Byte.MAX_VALUE + 1, false, true, true, true, true, true);
assertNumber((int) Short.MAX_VALUE, false, true, true, true, true, true);
assertNumber(Short.MAX_VALUE + 1, false, false, true, true, true, true);
assertNumber(1 << 24, false, false, true, true, true, true);
assertNumber((1 << 24) + 1, false, false, true, true, false, true);
assertNumber(1 << 25, false, false, true, true, true, true);
assertNumber(Integer.MAX_VALUE, false, false, true, true, true, true);
}
@Test
public void testLongDefault() throws InteropException {
assertNumber(Long.MIN_VALUE, false, false, false, true, true, true);
assertNumber((long) Integer.MIN_VALUE - 1, false, false, false, true, false, true);
assertNumber((long) Integer.MIN_VALUE, false, false, true, true, true, true);
assertNumber((long) Short.MIN_VALUE - 1, false, false, true, true, true, true);
assertNumber((long) Short.MIN_VALUE, false, true, true, true, true, true);
assertNumber((long) Byte.MIN_VALUE - 1, false, true, true, true, true, true);
assertNumber((long) Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber(0L, true, true, true, true, true, true);
assertNumber((long) Byte.MAX_VALUE, true, true, true, true, true, true);
assertNumber((long) Byte.MAX_VALUE + 1, false, true, true, true, true, true);
assertNumber((long) Short.MAX_VALUE, false, true, true, true, true, true);
assertNumber((long) Short.MAX_VALUE + 1, false, false, true, true, true, true);
assertNumber((long) Integer.MAX_VALUE, false, false, true, true, false, true);
assertNumber(1L << 24, false, false, true, true, true, true);
assertNumber((1L << 24) + 1, false, false, true, true, false, true);
assertNumber(1L << 25, false, false, true, true, true, true);
assertNumber((1L << 53) + 1, false, false, false, true, false, false);
assertNumber(1L << 54, false, false, false, true, true, true);
assertNumber(Long.MAX_VALUE, false, false, false, true, true, true);
}
@Test
public void testFloatDefault() throws InteropException {
assertNumber(Float.NEGATIVE_INFINITY, false, false, false, false, true, true);
assertNumber((float) Long.MIN_VALUE, false, false, false, false, true, true);
assertNumber((float) Integer.MIN_VALUE - 1, false, false, false, false, true, true);
assertNumber((float) Integer.MIN_VALUE, false, false, false, false, true, true);
assertNumber((float) Short.MIN_VALUE - 1, false, false, true, true, true, true);
assertNumber((float) Short.MIN_VALUE, false, true, true, true, true, true);
assertNumber((float) Byte.MIN_VALUE - 1, false, true, true, true, true, true);
assertNumber((float) Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber(-0.0f, false, false, false, false, true, true);
assertNumber(0.0f, true, true, true, true, true, true);
assertNumber((float) Byte.MAX_VALUE, true, true, true, true, true, true);
assertNumber((float) Byte.MAX_VALUE + 1, false, true, true, true, true, true);
assertNumber((float) Short.MAX_VALUE, false, true, true, true, true, true);
assertNumber((float) Short.MAX_VALUE + 1, false, false, true, true, true, true);
assertNumber((float) Integer.MAX_VALUE, false, false, false, false, true, true);
assertNumber((float) Long.MAX_VALUE, false, false, false, false, true, true);
assertNumber(Float.POSITIVE_INFINITY, false, false, false, false, true, true);
assertNumber(Float.NaN, false, false, false, false, true, true);
assertNumber(Float.MIN_VALUE, false, false, false, false, true, true);
assertNumber(Float.MIN_NORMAL, false, false, false, false, true, true);
assertNumber(Float.MAX_VALUE, false, false, false, false, true, true);
}
@Test
public void testDoubleDefault() throws InteropException {
assertNumber(Double.NEGATIVE_INFINITY, false, false, false, false, true, true);
assertNumber((double) Long.MIN_VALUE, false, false, false, false, true, true);
assertNumber((double) Integer.MIN_VALUE - 1, false, false, false, true, false, true);
assertNumber((double) Integer.MIN_VALUE, false, false, true, true, true, true);
assertNumber((double) Short.MIN_VALUE - 1, false, false, true, true, true, true);
assertNumber((double) Short.MIN_VALUE, false, true, true, true, true, true);
assertNumber((double) Byte.MIN_VALUE - 1, false, true, true, true, true, true);
assertNumber((double) Byte.MIN_VALUE, true, true, true, true, true, true);
assertNumber(-0.0d, false, false, false, false, true, true);
assertNumber(0.0d, true, true, true, true, true, true);
assertNumber((double) Byte.MAX_VALUE, true, true, true, true, true, true);
assertNumber((double) Byte.MAX_VALUE + 1, false, true, true, true, true, true);
assertNumber((double) Short.MAX_VALUE, false, true, true, true, true, true);
assertNumber((double) Short.MAX_VALUE + 1, false, false, true, true, true, true);
assertNumber((double) Integer.MAX_VALUE, false, false, true, true, false, true);
assertNumber((double) Long.MAX_VALUE, false, false, false, false, true, true);
assertNumber(Double.POSITIVE_INFINITY, false, false, false, false, true, true);
assertNumber(Double.NaN, false, false, false, false, true, true);
assertNumber(Double.MIN_VALUE, false, false, false, false, false, true);
assertNumber(Double.MIN_NORMAL, false, false, false, false, false, true);
assertNumber(Double.MAX_VALUE, false, false, false, false, false, true);
}
@Test
public void testStringDefaults() throws InteropException {
assertString("foo", "foo");
assertString("bar", "bar");
}
@Test
public void testCharacterDefaults() throws InteropException {
assertString('a', "a");
assertString('b', "b");
}
private void assertString(Object v, String expectedString) throws UnsupportedMessageException, InteropException {
InteropLibrary library = createLibrary(InteropLibrary.class, v);
assertTrue(library.isString(v));
assertEquals(expectedString, library.asString(v));
assertNoBoolean(v);
assertNotNull(v);
assertNoObject(v);
assertNoArray(v);
assertNoNumber(v);
assertNoNative(v);
assertNotExecutable(v);
assertNotInstantiable(v);
assertNoMetaObject(v);
assertHasNoMetaObject(v);
assertNoDate(v);
assertNoTime(v);
assertNoTimeZone(v);
assertNoDuration(v);
assertNoSourceLocation(v);
assertNoLanguage(v);
assertNoIdentity(v);
}
private void assertNumber(Object v, boolean supportsByte, boolean supportsShort,
boolean supportsInt, boolean supportsLong, boolean supportsFloat, boolean supportsDouble) throws InteropException {
Object expectedValue = v;
InteropLibrary l = createLibrary(InteropLibrary.class, v);
assertTrue(l.isNumber(v));
assertEquals(supportsByte, l.fitsInByte(v));
assertEquals(supportsShort, l.fitsInShort(v));
assertEquals(supportsInt, l.fitsInInt(v));
assertEquals(supportsLong, l.fitsInLong(v));
assertEquals(supportsFloat, l.fitsInFloat(v));
assertEquals(supportsDouble, l.fitsInDouble(v));
if (supportsByte) {
assertEquals(((Number) expectedValue).byteValue(), l.asByte(v));
} else {
assertUnsupported(() -> l.asByte(v));
}
if (supportsShort) {
assertEquals(((Number) expectedValue).shortValue(), l.asShort(v));
} else {
assertUnsupported(() -> l.asShort(v));
}
if (supportsInt) {
assertEquals(((Number) expectedValue).intValue(), l.asInt(v));
} else {
assertUnsupported(() -> l.asInt(v));
}
if (supportsLong) {
assertEquals(((Number) expectedValue).longValue(), l.asLong(v));
} else {
assertUnsupported(() -> l.asLong(v));
}
if (supportsFloat) {
assertEquals(((Number) expectedValue).floatValue(), l.asFloat(v), 0);
} else {
assertUnsupported(() -> l.asFloat(v));
}
if (supportsDouble) {
assertEquals(((Number) expectedValue).doubleValue(), l.asDouble(v), 0);
} else {
assertUnsupported(() -> l.asDouble(v));
}
assertEquals(v.toString(), l.toDisplayString(v));
assertNoBoolean(v);
assertNotNull(v);
assertNoObject(v);
assertNoArray(v);
assertNoString(v);
assertNoNative(v);
assertNotExecutable(v);
assertNotInstantiable(v);
assertNoMetaObject(v);
assertHasNoMetaObject(v);
assertNoDate(v);
assertNoTime(v);
assertNoTimeZone(v);
assertNoDuration(v);
assertNoSourceLocation(v);
assertNoLanguage(v);
assertNoIdentity(v);
}
@Test
public void testObjectDefaults() {
AtomicBoolean toStringInvoked = new AtomicBoolean();
Object v = new TruffleObject() {
@Override
public String toString() {
toStringInvoked.set(true);
return super.toString();
}
};
InteropLibrary l = createLibrary(InteropLibrary.class, v);
String expectedToString = v.toString();
toStringInvoked.set(false);
assertEquals(expectedToString, l.toDisplayString(v));
assertFalse(toStringInvoked.get());
assertNoTypes(v);
}
public static class MetaDataLegacyObject implements TruffleObject {
final SourceSection section;
final Object metaObject;
MetaDataLegacyObject(SourceSection section, Object metaObject) {
this.section = section;
this.metaObject = metaObject;
}
}
public static class MetaDataLegacyOnlyLangauge implements TruffleObject {
MetaDataLegacyOnlyLangauge() {
}
}
@Test
public void testMetaDataLegacyBehavior() throws InteropException {
setupEnv(Context.create(), new ProxyLanguage() {
@Override
protected boolean isObjectOfLanguage(Object object) {
return object instanceof MetaDataLegacyObject || object instanceof MetaDataLegacyOnlyLangauge;
}
@Override
protected SourceSection findSourceLocation(LanguageContext c, Object value) {
if (value instanceof MetaDataLegacyObject) {
return ((MetaDataLegacyObject) value).section;
}
return null;
}
@Override
protected Object findMetaObject(LanguageContext c, Object value) {
if (value instanceof MetaDataLegacyObject) {
return ((MetaDataLegacyObject) value).metaObject;
}
return null;
}
@Override
protected String toString(LanguageContext c, Object value) {
if (value instanceof MetaDataLegacyObject) {
return "MetaDataLegacyObject";
}
return super.toString(c, value);
}
});
SourceSection section = Source.newBuilder(ProxyLanguage.ID, "", "").build().createUnavailableSection();
Object v1 = new MetaDataLegacyObject(section, "meta-object");
InteropLibrary libV1 = createLibrary(InteropLibrary.class, v1);
assertTrue(libV1.hasLanguage(v1));
assertSame(ProxyLanguage.class, libV1.getLanguage(v1));
assertTrue(libV1.hasMetaObject(v1));
Object metaObject = libV1.getMetaObject(v1);
InteropLibrary metaObjectInterop = createLibrary(InteropLibrary.class, metaObject);
assertTrue(metaObjectInterop.isMetaObject(metaObject));
assertTrue(metaObjectInterop.isMetaInstance(metaObject, v1));
assertEquals("meta-object", metaObjectInterop.toDisplayString(metaObject));
assertEquals("meta-object", metaObjectInterop.getMetaSimpleName(metaObject));
assertEquals("meta-object", metaObjectInterop.getMetaQualifiedName(metaObject));
assertTrue(libV1.hasSourceLocation(v1));
assertSame(section, libV1.getSourceLocation(v1));
assertEquals("MetaDataLegacyObject", libV1.toDisplayString(v1));
assertNoBoolean(v1);
assertNotNull(v1);
assertNoObject(v1);
assertNoArray(v1);
assertNoString(v1);
assertNoNumber(v1);
assertNoNative(v1);
assertNotExecutable(v1);
assertNotInstantiable(v1);
assertNoMetaObject(v1);
assertNoDate(v1);
assertNoTime(v1);
assertNoTimeZone(v1);
assertNoDuration(v1);
Object v2 = new MetaDataLegacyOnlyLangauge();
InteropLibrary libV2 = createLibrary(InteropLibrary.class, v2);
assertTrue(libV2.hasLanguage(v2));
assertSame(ProxyLanguage.class, libV2.getLanguage(v2));
assertFalse(libV2.hasMetaObject(v2));
assertFails(() -> libV2.getMetaObject(v2), UnsupportedMessageException.class);
assertFalse(libV2.hasSourceLocation(v2));
assertFails(() -> libV2.getSourceLocation(v2), UnsupportedMessageException.class);
assertEquals(v2.toString(), libV2.toDisplayString(v2));
assertNoBoolean(v2);
assertNotNull(v2);
assertNoObject(v2);
assertNoArray(v2);
assertNoString(v2);
assertNoNumber(v2);
assertNoNative(v2);
assertNotExecutable(v2);
assertNotInstantiable(v2);
assertNoMetaObject(v2);
assertHasNoMetaObject(v2);
assertNoDate(v2);
assertNoTime(v2);
assertNoTimeZone(v2);
assertNoDuration(v2);
assertNoSourceLocation(v2);
}
private void assertNoTypes(Object v) {
assertNoBoolean(v);
assertNotNull(v);
assertNoObject(v);
assertNoArray(v);
assertNoString(v);
assertNoNumber(v);
assertNoNative(v);
assertNotExecutable(v);
assertNotInstantiable(v);
assertNoMetaObject(v);
assertHasNoMetaObject(v);
assertNoDate(v);
assertNoTime(v);
assertNoTimeZone(v);
assertNoDuration(v);
assertNoSourceLocation(v);
assertNoLanguage(v);
assertNoIdentity(v);
}
@Test
public void testScopeDefault() {
Object v = new TruffleObject() {
};
InteropLibrary l = createLibrary(InteropLibrary.class, v);
assertFalse(l.isScope(v));
assertFalse(l.hasScopeParent(v));
assertFails(() -> l.getScopeParent(v), UnsupportedMessageException.class);
}
@Test
public void testExceptionDefaults() throws UnsupportedMessageException {
Object empty = new TruffleObject() {
};
InteropLibrary emptyLib = createLibrary(InteropLibrary.class, empty);
assertFalse(emptyLib.isException(empty));
assertFalse(emptyLib.hasExceptionCause(empty));
assertFalse(emptyLib.hasExceptionMessage(empty));
assertFalse(emptyLib.hasExceptionStackTrace(empty));
assertFails(() -> emptyLib.getExceptionCause(empty), UnsupportedMessageException.class);
assertFails(() -> emptyLib.getExceptionExitStatus(empty), UnsupportedMessageException.class);
assertFails(() -> emptyLib.isExceptionIncompleteSource(empty), UnsupportedMessageException.class);
assertFails(() -> emptyLib.getExceptionMessage(empty), UnsupportedMessageException.class);
assertFails(() -> emptyLib.getExceptionStackTrace(empty), UnsupportedMessageException.class);
assertFails(() -> emptyLib.getExceptionType(empty), UnsupportedMessageException.class);
AbstractTruffleException cause = new Exception("Cause Exception");
String message = "Enclosing exception";
AbstractTruffleException exception = new Exception(message, cause);
InteropLibrary exceptionLib = createLibrary(InteropLibrary.class, exception);
assertTrue(exceptionLib.isException(exception));
assertTrue(exceptionLib.hasExceptionCause(exception));
assertTrue(exceptionLib.hasExceptionMessage(exception));
assertTrue(exceptionLib.hasExceptionStackTrace(exception));
assertEquals(cause, exceptionLib.getExceptionCause(exception));
assertEquals(message, exceptionLib.getExceptionMessage(exception));
assertEquals(ExceptionType.RUNTIME_ERROR, exceptionLib.getExceptionType(exception));
assertFalse(exceptionLib.isExceptionIncompleteSource(exception));
assertFails(() -> exceptionLib.getExceptionExitStatus(exception), UnsupportedMessageException.class);
exceptionLib.getExceptionStackTrace(exception);
LegacyCatchableException legacyCatchableException = new LegacyCatchableException(message);
InteropLibrary legacyCatchableExceptionLib = createLibrary(InteropLibrary.class, legacyCatchableException);
assertTrue(legacyCatchableExceptionLib.isException(legacyCatchableException));
assertFalse(legacyCatchableExceptionLib.hasExceptionCause(legacyCatchableException));
assertTrue(legacyCatchableExceptionLib.hasExceptionMessage(legacyCatchableException));
assertTrue(legacyCatchableExceptionLib.hasExceptionStackTrace(legacyCatchableException));
assertFails(() -> legacyCatchableExceptionLib.getExceptionCause(legacyCatchableException), UnsupportedMessageException.class);
assertEquals(message, legacyCatchableExceptionLib.getExceptionMessage(legacyCatchableException));
assertEquals(ExceptionType.RUNTIME_ERROR, legacyCatchableExceptionLib.getExceptionType(legacyCatchableException));
assertFails(() -> legacyCatchableExceptionLib.getExceptionExitStatus(legacyCatchableException), UnsupportedMessageException.class);
assertFalse(legacyCatchableExceptionLib.isExceptionIncompleteSource(legacyCatchableException));
legacyCatchableExceptionLib.getExceptionStackTrace(legacyCatchableException);
LegacyUncatchableException legacyUncatchableException = new LegacyUncatchableException();
InteropLibrary legacyUncatchableExceptionLib = createLibrary(InteropLibrary.class, legacyUncatchableException);
assertFalse(legacyUncatchableExceptionLib.isException(legacyUncatchableException));
assertFalse(legacyUncatchableExceptionLib.hasExceptionCause(legacyUncatchableException));
assertFalse(legacyUncatchableExceptionLib.hasExceptionMessage(legacyUncatchableException));
assertFalse(legacyUncatchableExceptionLib.hasExceptionStackTrace(legacyUncatchableException));
assertFails(() -> legacyUncatchableExceptionLib.getExceptionCause(legacyUncatchableException), UnsupportedMessageException.class);
assertFails(() -> legacyUncatchableExceptionLib.getExceptionMessage(legacyUncatchableException), UnsupportedMessageException.class);
assertFails(() -> legacyUncatchableExceptionLib.getExceptionType(legacyUncatchableException), UnsupportedMessageException.class);
assertFails(() -> legacyUncatchableExceptionLib.getExceptionExitStatus(legacyUncatchableException), UnsupportedMessageException.class);
assertFails(() -> legacyUncatchableExceptionLib.isExceptionIncompleteSource(legacyUncatchableException), UnsupportedMessageException.class);
assertFails(() -> legacyUncatchableExceptionLib.getExceptionStackTrace(legacyUncatchableException), UnsupportedMessageException.class);
LegacyInternalError legacyInternalError = new LegacyInternalError(message);
InteropLibrary legacyInternalErrorLib = createLibrary(InteropLibrary.class, legacyInternalError);
assertFalse(legacyInternalErrorLib.isException(legacyInternalError));
assertFalse(legacyInternalErrorLib.hasExceptionCause(legacyInternalError));
assertFalse(legacyInternalErrorLib.hasExceptionMessage(legacyInternalError));
assertFalse(legacyInternalErrorLib.hasExceptionStackTrace(legacyInternalError));
assertFails(() -> legacyInternalErrorLib.getExceptionCause(legacyInternalError), UnsupportedMessageException.class);
assertFails(() -> legacyInternalErrorLib.getExceptionMessage(legacyInternalError), UnsupportedMessageException.class);
assertFails(() -> legacyInternalErrorLib.getExceptionType(legacyInternalError), UnsupportedMessageException.class);
assertFails(() -> legacyInternalErrorLib.getExceptionExitStatus(legacyInternalError), UnsupportedMessageException.class);
assertFails(() -> legacyInternalErrorLib.isExceptionIncompleteSource(legacyInternalError), UnsupportedMessageException.class);
assertFails(() -> legacyInternalErrorLib.getExceptionStackTrace(legacyInternalError), UnsupportedMessageException.class);
}
@SuppressWarnings("serial")
private static final class Exception extends AbstractTruffleException {
Exception(String message) {
super(message);
}
Exception(String message, Throwable cause) {
super(message, cause, UNLIMITED_STACK_TRACE, null);
}
}
@SuppressWarnings({"serial", "deprecation"})
private static final class LegacyCatchableException extends RuntimeException implements com.oracle.truffle.api.TruffleException {
LegacyCatchableException(String message) {
super(message);
}
@Override
public Node getLocation() {
return null;
}
}
@SuppressWarnings({"serial", "deprecation"})
private static final class LegacyUncatchableException extends ThreadDeath implements com.oracle.truffle.api.TruffleException {
LegacyUncatchableException() {
}
@Override
public Node getLocation() {
return null;
}
}
@SuppressWarnings({"serial", "deprecation"})
private static final class LegacyInternalError extends RuntimeException implements com.oracle.truffle.api.TruffleException {
LegacyInternalError(String message) {
super(message);
}
@Override
public Node getLocation() {
return null;
}
@Override
public boolean isInternalError() {
return true;
}
}
}