package com.oracle.truffle.api.test.polyglot;
import static com.oracle.truffle.tck.tests.ValueAssert.assertValue;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ARRAY_ELEMENTS;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DATE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DURATION;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.EXCEPTION;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.EXECUTABLE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.HOST_OBJECT;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.INSTANTIABLE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.MEMBERS;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.META;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NULL;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NUMBER;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.PROXY_OBJECT;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIME;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIMEZONE;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.HostAccess.Implementable;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.TypeLiteral;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyArray;
import org.graalvm.polyglot.proxy.ProxyDate;
import org.graalvm.polyglot.proxy.ProxyDuration;
import org.graalvm.polyglot.proxy.ProxyExecutable;
import org.graalvm.polyglot.proxy.ProxyInstant;
import org.graalvm.polyglot.proxy.ProxyInstantiable;
import org.graalvm.polyglot.proxy.ProxyObject;
import org.graalvm.polyglot.proxy.ProxyTime;
import org.graalvm.polyglot.proxy.ProxyTimeZone;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ComparisonFailure;
import org.junit.Test;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.tck.tests.ValueAssert;
import com.oracle.truffle.tck.tests.ValueAssert.Trait;
public class ValueAPITest {
private Context context;
@Before
public void setUp() {
context = Context.newBuilder().allowHostAccess(HostAccess.ALL).build();
}
@After
public void tearDown() {
context.close();
}
@Test
public void testGetContext() {
assertNull(Value.asValue(null).getContext());
assertNull(Value.asValue("").getContext());
assertNull(Value.asValue(42).getContext());
assertNull(Value.asValue(Instant.now()).getContext());
assertNull(Value.asValue(new EmptyObject()).getContext());
assertNull(Value.asValue(new Members()).getContext());
Context creator = Context.create();
creator.enter();
Context current = Context.getCurrent();
creator.leave();
assertNotSame(creator, current);
assertEquals(creator, current);
assertSame(current, creator.asValue(null).getContext());
assertSame(current, creator.asValue("").getContext());
assertSame(current, creator.asValue(42).getContext());
assertSame(current, creator.asValue(Instant.now()).getContext());
assertSame(current, creator.asValue(new EmptyObject()).getContext());
assertSame(current, creator.asValue(new Members()).getContext());
}
@Test
public void testInstantiate() {
Value classValue = context.asValue(java.util.Date.class);
Value dateInstance = classValue.newInstance();
Date date = dateInstance.asHostObject();
assertNotNull(date);
long ms = 1_000_000_000L;
dateInstance = classValue.newInstance(ms);
date = dateInstance.asHostObject();
assertEquals(ms, date.getTime());
}
private static final Object[] STRINGS = new Object[]{
"", 'a', "a", "foo",
};
@SuppressWarnings("deprecation")
@Test
public void testString() {
for (Object string : STRINGS) {
assertValue(context.asValue(string), STRING);
assertValue(context.asValue(new StringWrapper(string.toString())), STRING);
}
}
@Test
public void testDatesTimesZonesAndDuration() {
assertValue(context.asValue(LocalDate.now()), HOST_OBJECT, MEMBERS, DATE, EXECUTABLE);
assertValue(context.asValue(LocalTime.now()), HOST_OBJECT, MEMBERS, TIME, EXECUTABLE);
assertValue(context.asValue(LocalDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, EXECUTABLE);
assertValue(context.asValue(Instant.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, EXECUTABLE);
assertValue(context.asValue(ZonedDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE);
assertValue(context.asValue(ZoneId.of("UTC")), HOST_OBJECT, MEMBERS, TIMEZONE);
assertValue(context.asValue(Date.from(Instant.now())), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE);
assertValue(context.asValue(Duration.ofMillis(100)), HOST_OBJECT, MEMBERS, DURATION);
assertValue(context.asValue(ProxyDate.from(LocalDate.now())), DATE, PROXY_OBJECT);
assertValue(context.asValue(ProxyTime.from(LocalTime.now())), TIME, PROXY_OBJECT);
assertValue(context.asValue(ProxyInstant.from(Instant.now())), DATE, TIME, TIMEZONE, PROXY_OBJECT);
assertValue(context.asValue(ProxyTimeZone.from(ZoneId.of("UTC"))), TIMEZONE, PROXY_OBJECT);
assertValue(context.asValue(ProxyDuration.from(Duration.ofMillis(100))), DURATION, PROXY_OBJECT);
}
private static final Number[] NUMBERS = new Number[]{
(byte) 0, (byte) 1, Byte.MAX_VALUE, Byte.MIN_VALUE,
(short) 0, (short) 1, Short.MAX_VALUE, Short.MIN_VALUE,
0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE,
0L, 1L, Long.MAX_VALUE, Long.MIN_VALUE,
0f, 0.24f, -0f, Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.MIN_NORMAL,
0d, 0.24d, -0d, Double.MAX_VALUE, Double.MIN_VALUE, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MIN_NORMAL,
};
@SuppressWarnings("deprecation")
@Test
public void testNumbers() {
for (Number number : NUMBERS) {
assertValue(context.asValue(number), NUMBER);
assertValue(context.asValue(new NumberWrapper(number)), NUMBER);
}
}
private static final boolean[] BOOLEANS = new boolean[]{
false, true,
};
@SuppressWarnings("deprecation")
@Test
public void testBooleans() {
for (boolean bool : BOOLEANS) {
assertValue(context.asValue(bool), BOOLEAN);
assertValue(context.asValue(new BooleanWrapper(bool)), BOOLEAN);
}
}
@Test
public void testNull() {
assertValue(context.asValue(null), HOST_OBJECT, NULL);
}
private static final Object[] HOST_OBJECTS = new Object[]{
new ArrayList<>(),
new HashMap<>(),
new EmptyObject(),
new PrivateObject(),
new FieldAccess(),
new JavaSuperClass(),
new BigInteger("42"),
new BigDecimal("42"),
new Function<Object, Object>() {
public Object apply(Object t) {
return t;
}
}, new Supplier<String>() {
public String get() {
return "foobar";
}
}, BigDecimal.class, Class.class, Proxy.newProxyInstance(ValueAPITest.class.getClassLoader(), new Class<?>[]{ProxyInterface.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "foobar":
return args[0];
case "toString":
return "Proxy";
case "equals":
return proxy == args[0];
case "hashCode":
return System.identityHashCode(proxy);
default:
throw new UnsupportedOperationException(method.getName());
}
}
})};
@Test
public void testHostObject() {
assertTrue(context.asValue(new EmptyObject()).getMemberKeys().isEmpty());
assertTrue(context.asValue(new PrivateObject()).getMemberKeys().isEmpty());
for (Object value : HOST_OBJECTS) {
List<Trait> expectedTraits = new ArrayList<>();
expectedTraits.add(MEMBERS);
expectedTraits.add(HOST_OBJECT);
if (value instanceof Supplier || value instanceof Function) {
expectedTraits.add(EXECUTABLE);
}
if (value instanceof List) {
expectedTraits.add(ARRAY_ELEMENTS);
}
if (value instanceof Class && value != Class.class) {
expectedTraits.add(INSTANTIABLE);
}
if (value instanceof Class) {
expectedTraits.add(META);
}
assertValue(context.asValue(value), expectedTraits.toArray(new Trait[0]));
}
}
private static final Object[] ARRAYS = new Object[]{
new boolean[]{true, false, true},
new char[]{'a', 'b', 'c'},
new byte[]{42, 42, 42},
new short[]{42, 42, 42},
new int[]{42, 42, 42},
new long[]{42, 42, 42},
new float[]{42, 42, 42},
new double[]{42, 42, 42},
new Boolean[]{true, false, true},
new Character[]{'a', 'b', 'c'},
new Byte[]{42, 42, 42},
new Short[]{42, 42, 42},
new Integer[]{42, 42, 42},
new Long[]{42L, 42L, 42L},
new Float[]{42f, 42f, 42f},
new Double[]{42d, 42d, 42d},
new Object[]{true, 'a', "ab", (byte) 42, (short) 42, 42, 42L, 42f, 42d},
new String[]{"a", "b", "c"},
new CharSequence[]{"a"},
new ArrayList<>(Arrays.asList("a", 42)),
new DummyList()
};
@Test
public void testArrays() {
for (Object array : ARRAYS) {
assertValue(context.asValue(array), ARRAY_ELEMENTS, HOST_OBJECT, MEMBERS);
}
}
@Test
public void testListRemove() {
List<Object> list = new ArrayList<>(Arrays.asList("a", "b", 42, 43));
Value vlist = context.asValue(list);
assertEquals(4, vlist.getArraySize());
boolean success = vlist.removeArrayElement(1);
assertTrue(success);
assertEquals(3, list.size());
assertEquals(3, vlist.getArraySize());
}
@Test
public void testComplexGenericCoercion() {
TypeLiteral<List<Map<Integer, Map<String, Object[]>>>> literal = new TypeLiteral<List<Map<Integer, Map<String, Object[]>>>>() {
};
Map<String, Object> map = new HashMap<>();
map.put("foobar", new Object[]{"baz"});
Object[] array = new Object[]{ProxyArray.fromArray(ProxyObject.fromMap(map))};
List<Map<Integer, Map<String, Object[]>>> value = context.asValue(array).as(literal);
assertEquals("baz", value.get(0).get(0).get("foobar")[0]);
}
@Test
public void testComplexGenericCoercion2() {
TypeLiteral<List<Map<String, Map<String, Object[]>>>> literal = new TypeLiteral<List<Map<String, Map<String, Object[]>>>>() {
};
Object[] array = new Object[]{ProxyObject.fromMap(Collections.singletonMap("foo", ProxyObject.fromMap(Collections.singletonMap("bar", new Object[]{"baz"}))))};
List<Map<String, Map<String, Object[]>>> value = context.asValue(array).as(literal);
assertEquals("baz", value.get(0).get("foo").get("bar")[0]);
}
public interface JavaInterface {
}
@FunctionalInterface
public interface JavaFunctionalInterface {
void foo();
}
public static class JavaSuperClass {
}
public static class JavaClass extends JavaSuperClass implements JavaInterface, JavaFunctionalInterface {
public void foo() {
}
}
@SuppressWarnings("unchecked")
@Test
public void testObjectCoercion() {
objectCoercionTest(null, Object.class, null);
objectCoercionTest(null, String.class, null);
objectCoercionTest(null, Boolean.class, null);
objectCoercionTest(null, Boolean.class, null);
objectCoercionTest(true, Boolean.class, null);
objectCoercionTest("foo", String.class, null);
objectCoercionTest('c', Character.class, null);
objectCoercionTest((byte) 42, Byte.class, null);
objectCoercionTest((short) 42, Short.class, null);
objectCoercionTest(42, Integer.class, null);
objectCoercionTest((long) 42, Long.class, null);
objectCoercionTest(42.5d, Double.class, null);
objectCoercionTest(42.5f, Float.class, null);
objectCoercionTest(new JavaClass(), JavaClass.class, null);
objectCoercionTest(new JavaClass(), JavaSuperClass.class, null);
objectCoercionTest(new JavaClass(), JavaInterface.class, null);
objectCoercionTest(new JavaClass(), JavaFunctionalInterface.class, null);
objectCoercionTest(new JavaClass(), Object.class, null);
Map<String, Object> map = new HashMap<>();
map.put("foobar", "baz");
map.put("foobar2", "baz2");
objectCoercionTest(ProxyObject.fromMap(map), Map.class,
(v) -> assertEquals("baz", v.get("foobar")));
objectCoercionTest(ProxyObject.fromMap(map), Map.class, (v) -> {
assertNull(v.remove("notAMember"));
assertEquals("baz", v.remove("foobar"));
assertFalse(v.remove("foobar2", "baz"));
assertFalse(v.remove("foobar", "baz2"));
assertTrue(v.remove("foobar2", "baz2"));
assertTrue(map.isEmpty());
map.put("foobar", "baz");
map.put("foobar2", "baz2");
});
ProxyArray array = ProxyArray.fromArray(42, 42, 42);
objectCoercionTest(array, List.class,
(v) -> assertEquals(42, v.get(2)));
List<Object> arrayList = new ArrayList<>();
ProxyArray list = ProxyArray.fromList(arrayList);
objectCoercionTest(list, List.class, (v) -> {
arrayList.addAll(Arrays.asList(41, 42));
assertEquals(42, v.remove(1));
assertTrue(v.remove((Object) 41));
assertFalse(v.remove((Object) 14));
});
ArrayElements arrayElements = new ArrayElements();
arrayElements.array.add(42);
objectCoercionTest(arrayElements, List.class, (v) -> {
assertEquals(42, v.get(0));
assertEquals(1, v.size());
assertFalse(v instanceof Function);
Value value = context.asValue(v);
assertFalse(value.canExecute());
assertFalse(value.canInstantiate());
assertFalse(value.hasMembers());
assertTrue(value.hasArrayElements());
});
MembersAndArray mapAndArray = new MembersAndArray();
mapAndArray.map.put("foo", "bar");
mapAndArray.array.add(42);
objectCoercionTest(mapAndArray, Map.class, (v) -> {
assertNull(v.get(0L));
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertFalse(v instanceof Function);
Value value = context.asValue(v);
assertFalse(value.canExecute());
assertFalse(value.canInstantiate());
assertTrue(value.hasMembers());
assertTrue(value.hasArrayElements());
});
MembersAndInstantiable membersAndInstantiable = new MembersAndInstantiable();
membersAndInstantiable.map.put("foo", "bar");
membersAndInstantiable.instantiableResult = "foobarbaz";
objectCoercionTest(membersAndInstantiable, Map.class, (v) -> {
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertTrue(v instanceof Function);
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertFalse(value.canExecute());
assertTrue(value.canInstantiate());
assertTrue(value.hasMembers());
assertFalse(value.hasArrayElements());
});
MembersAndExecutable membersAndExecutable = new MembersAndExecutable();
membersAndExecutable.map.put("foo", "bar");
membersAndExecutable.executableResult = "foobarbaz";
objectCoercionTest(membersAndExecutable, Map.class, (v) -> {
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertTrue(v instanceof Function);
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertTrue(value.canExecute());
assertFalse(value.canInstantiate());
assertTrue(value.hasMembers());
assertFalse(value.hasArrayElements());
});
MembersAndArrayAndExecutable mapAndArrayAndExecutable = new MembersAndArrayAndExecutable();
mapAndArrayAndExecutable.map.put("foo", "bar");
mapAndArrayAndExecutable.array.add(42);
mapAndArrayAndExecutable.executableResult = "foobarbaz";
objectCoercionTest(mapAndArrayAndExecutable, Map.class, (v) -> {
assertNull(v.get(0L));
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertTrue(v instanceof Function);
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertTrue(value.canExecute());
assertFalse(value.canInstantiate());
assertTrue(value.hasMembers());
assertTrue(value.hasArrayElements());
});
MembersAndArrayAndInstantiable mapAndArrayAndInstantiable = new MembersAndArrayAndInstantiable();
mapAndArrayAndInstantiable.map.put("foo", "bar");
mapAndArrayAndInstantiable.array.add(42);
mapAndArrayAndInstantiable.instantiableResult = "foobarbaz";
objectCoercionTest(mapAndArrayAndInstantiable, Map.class, (v) -> {
assertNull(v.get(0L));
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertTrue(v instanceof Function);
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertFalse(value.canExecute());
assertTrue(value.canInstantiate());
assertTrue(value.hasMembers());
assertTrue(value.hasArrayElements());
});
MembersAndArrayAndExecutableAndInstantiable mapAndArrayAndExecutableAndInstantiable = new MembersAndArrayAndExecutableAndInstantiable();
mapAndArrayAndExecutableAndInstantiable.map.put("foo", "bar");
mapAndArrayAndExecutableAndInstantiable.array.add(42);
mapAndArrayAndExecutableAndInstantiable.executableResult = "foobarbaz";
objectCoercionTest(mapAndArrayAndExecutableAndInstantiable, Map.class, (v) -> {
assertNull(v.get(0L));
assertEquals("bar", v.get("foo"));
assertEquals(1, v.size());
assertTrue(v instanceof Function);
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertTrue(value.canExecute());
assertTrue(value.canInstantiate());
assertTrue(value.hasMembers());
assertTrue(value.hasArrayElements());
});
Executable exectable = new Executable();
exectable.executableResult = "foobarbaz";
objectCoercionTest(exectable, Function.class, (v) -> {
assertEquals("foobarbaz", ((Function<Object, Object>) v).apply(new Object[0]));
Value value = context.asValue(v);
assertTrue(value.canExecute());
assertFalse(value.canInstantiate());
assertFalse(value.hasMembers());
assertFalse(value.hasArrayElements());
});
MembersAndInvocable invocable = new MembersAndInvocable();
invocable.invokeMember = "foo";
invocable.invocableResult = "foobarbaz";
objectCoercionTest(invocable, Map.class, (v) -> {
Value value = context.asValue(v);
assertTrue(value.canInvokeMember("foo"));
assertEquals("foobarbaz", value.invokeMember("foo").asString());
}, false);
}
private <T> void objectCoercionTest(Object value, Class<T> expectedType, Consumer<T> validator) {
objectCoercionTest(value, expectedType, validator, true);
}
@SuppressWarnings({"unchecked"})
private <T> void objectCoercionTest(Object value, Class<T> expectedType, Consumer<T> validator, boolean valueTest) {
Value coerce = context.asValue(new CoerceObject()).getMember("coerce");
T result = (T) context.asValue(value).as(Object.class);
if (result != null) {
assertTrue("expected " + expectedType + " but was " + result.getClass(), expectedType.isInstance(result));
} else if (value != null) {
fail("expected " + expectedType + " but was null");
}
if (validator == null) {
assertEquals(value, result);
} else {
validator.accept(result);
coerce.execute(value, validator);
}
if (valueTest) {
assertValue(context.asValue(value));
assertValue(context.asValue(result));
}
}
private static class DummyList extends DummyCollection implements List<Object> {
public boolean addAll(int index, Collection<? extends Object> c) {
return values.addAll(index, c);
}
public Object get(int index) {
return values.get(index);
}
public Object set(int index, Object element) {
return values.set(index, element);
}
public void add(int index, Object element) {
values.add(index, element);
}
public Object remove(int index) {
return values.remove(index);
}
public int indexOf(Object o) {
return values.indexOf(o);
}
public int lastIndexOf(Object o) {
return values.lastIndexOf(o);
}
public ListIterator<Object> listIterator() {
return values.listIterator();
}
public ListIterator<Object> listIterator(int index) {
return values.listIterator(index);
}
public List<Object> subList(int fromIndex, int toIndex) {
return values.subList(fromIndex, toIndex);
}
}
private static class DummyCollection implements Collection<Object> {
List<Object> values = Arrays.asList("a", 42);
public java.util.Iterator<Object> iterator() {
return values.iterator();
}
public int size() {
return values.size();
}
public boolean isEmpty() {
return values.isEmpty();
}
public boolean contains(Object o) {
return values.contains(o);
}
public Object[] toArray() {
return values.toArray();
}
public <T> T[] toArray(T[] a) {
return values.toArray(a);
}
public boolean add(Object e) {
return values.add(e);
}
public boolean remove(Object o) {
return values.remove(o);
}
public boolean containsAll(Collection<?> c) {
return values.containsAll(c);
}
public boolean addAll(Collection<? extends Object> c) {
return values.addAll(c);
}
public boolean removeAll(Collection<?> c) {
return values.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return values.retainAll(c);
}
public void clear() {
values.clear();
}
}
public static class CoerceObject {
public <T> Object coerce(T value, Consumer<T> o) {
o.accept(value);
return value;
}
}
public static class EmptyObject {
}
private static class PrivateObject {
}
private static class FieldAccess {
@SuppressWarnings("unused") public EmptyObject member;
}
private interface ProxyInterface {
Object foobar(Object... args);
}
static class Members implements ProxyObject {
final Map<String, Object> map = new HashMap<>();
public Object getMember(String key) {
return map.get(key);
}
public ProxyArray getMemberKeys() {
return ProxyArray.fromArray(map.keySet().toArray());
}
public boolean hasMember(String key) {
return map.containsKey(key);
}
public void putMember(String key, Value value) {
map.put(key, value);
}
}
static class ArrayElements implements ProxyArray {
final List<Object> array = new ArrayList<>();
public Object get(long index) {
return array.get((int) index);
}
public void set(long index, Value value) {
array.set((int) index, value);
}
public long getSize() {
return array.size();
}
}
static class MembersAndArray extends Members implements ProxyObject, ProxyArray {
final List<Object> array = new ArrayList<>();
public Object get(long index) {
return array.get((int) index);
}
public void set(long index, Value value) {
array.set((int) index, value);
}
public long getSize() {
return array.size();
}
}
private static class MembersAndExecutable extends Members implements ProxyObject, ProxyExecutable {
Object executableResult;
public Object execute(Value... arguments) {
return executableResult;
}
}
private static class MembersAndInstantiable extends Members implements ProxyObject, ProxyInstantiable {
Object instantiableResult;
public Object newInstance(Value... arguments) {
return instantiableResult;
}
}
private static class MembersAndArrayAndExecutable extends MembersAndArray implements ProxyArray, ProxyObject, ProxyExecutable {
Object executableResult;
public Object execute(Value... arguments) {
return executableResult;
}
}
private static class MembersAndArrayAndInstantiable extends MembersAndArray implements ProxyArray, ProxyObject, ProxyInstantiable {
Object instantiableResult;
public Object newInstance(Value... arguments) {
return instantiableResult;
}
}
private static class MembersAndArrayAndExecutableAndInstantiable extends MembersAndArray implements ProxyArray, ProxyObject, ProxyInstantiable, ProxyExecutable {
Object executableResult;
Object instantiableResult;
public Object execute(Value... arguments) {
return executableResult;
}
public Object newInstance(Value... arguments) {
return instantiableResult;
}
}
private static class Executable implements ProxyExecutable {
Object executableResult;
public Object execute(Value... arguments) {
return executableResult;
}
}
@ExportLibrary(InteropLibrary.class)
static final class MembersAndInvocable implements TruffleObject {
String invokeMember;
Object invocableResult;
@SuppressWarnings("static-method")
@ExportMessage
boolean hasMembers() {
return true;
}
@ExportMessage
Object getMembers(@SuppressWarnings("unused") boolean internal) {
return new KeysArray(new String[]{invokeMember});
}
@ExportMessage
Object invokeMember(String member, @SuppressWarnings("unused") Object[] arguments) throws UnknownIdentifierException {
if (member.equals(invokeMember)) {
return invocableResult;
} else {
throw UnknownIdentifierException.create(member);
}
}
@ExportMessage
boolean isMemberInvocable(String member) {
return invokeMember.equals(member);
}
@ExportLibrary(InteropLibrary.class)
static final class KeysArray implements TruffleObject {
private final String[] keys;
KeysArray(String[] keys) {
this.keys = keys;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
boolean isArrayElementReadable(long index) {
return index >= 0 && index < keys.length;
}
@ExportMessage
long getArraySize() {
return keys.length;
}
@ExportMessage
Object readArrayElement(long index) throws InvalidArrayIndexException {
try {
return keys[(int) index];
} catch (IndexOutOfBoundsException e) {
CompilerDirectives.transferToInterpreter();
throw InvalidArrayIndexException.create(index);
}
}
}
}
@Test
public void testNullCoercionErrors() {
Value nullValue = context.asValue(null);
assertFails(() -> nullValue.asInt(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'int' using Value.asInt(). " +
"You can ensure that the operation is supported using Value.fitsInInt().");
assertFails(() -> nullValue.as(int.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'int'.");
assertFails(() -> nullValue.asByte(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'byte' using Value.asByte(). " +
"You can ensure that the operation is supported using Value.fitsInByte().");
assertFails(() -> nullValue.as(byte.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'byte'.");
assertFails(() -> nullValue.asShort(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'short' using Value.asShort(). " +
"You can ensure that the operation is supported using Value.fitsInShort().");
assertFails(() -> nullValue.as(short.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'short'.");
assertFails(() -> nullValue.asLong(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'long' using Value.asLong(). " +
"You can ensure that the operation is supported using Value.fitsInLong().");
assertFails(() -> nullValue.as(long.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'long'.");
assertFails(() -> nullValue.asFloat(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'float' using Value.asFloat(). " +
"You can ensure that the operation is supported using Value.fitsInFloat().");
assertFails(() -> nullValue.as(float.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'float'.");
assertFails(() -> nullValue.asDouble(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'double' using Value.asDouble(). " +
"You can ensure that the operation is supported using Value.fitsInDouble().");
assertFails(() -> nullValue.as(double.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'double'.");
assertFails(() -> nullValue.as(char.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'char'.");
assertFails(() -> nullValue.asBoolean(), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'boolean' using Value.asBoolean(). " +
"You can ensure that the operation is supported using Value.isBoolean().");
assertFails(() -> nullValue.as(boolean.class), NullPointerException.class,
"Cannot convert null value 'null'(language: Java) to Java type 'boolean'.");
}
@Test
public void testPrimitiveCoercionErrors() {
Value bigNumber = context.asValue(Long.MAX_VALUE);
assertFails(() -> bigNumber.asByte(), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'byte' using Value.asByte(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInByte().");
assertFails(() -> bigNumber.as(byte.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'byte': Invalid or lossy primitive coercion.");
assertFails(() -> bigNumber.as(Byte.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'java.lang.Byte': Invalid or lossy primitive coercion.");
assertFails(() -> bigNumber.asShort(), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'short' using Value.asShort(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInShort().");
assertFails(() -> bigNumber.as(short.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'short': Invalid or lossy primitive coercion.");
assertFails(() -> bigNumber.as(Short.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'java.lang.Short': Invalid or lossy primitive coercion.");
assertFails(() -> bigNumber.asInt(), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'int' using Value.asInt(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInInt().");
assertFails(() -> bigNumber.as(int.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'int': Invalid or lossy primitive coercion.");
assertFails(() -> bigNumber.as(Integer.class), ClassCastException.class,
"Cannot convert '9223372036854775807'(language: Java, type: java.lang.Long) to Java type 'java.lang.Integer': Invalid or lossy primitive coercion.");
Value nan = context.asValue(Double.NaN);
assertFails(() -> nan.asLong(), ClassCastException.class,
"Cannot convert 'NaN'(language: Java, type: java.lang.Double) to Java type 'long' using Value.asLong(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInLong().");
assertFails(() -> nan.as(long.class), ClassCastException.class,
"Cannot convert 'NaN'(language: Java, type: java.lang.Double) to Java type 'long': Invalid or lossy primitive coercion.");
assertFails(() -> nan.as(Long.class), ClassCastException.class,
"Cannot convert 'NaN'(language: Java, type: java.lang.Double) to Java type 'java.lang.Long': Invalid or lossy primitive coercion.");
Value nofloat = context.asValue(Double.MAX_VALUE);
assertFails(() -> nofloat.asFloat(), ClassCastException.class,
"Cannot convert '1.7976931348623157E308'(language: Java, type: java.lang.Double) to Java type 'float' using Value.asFloat(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInFloat().");
assertFails(() -> nofloat.as(float.class), ClassCastException.class,
"Cannot convert '1.7976931348623157E308'(language: Java, type: java.lang.Double) to Java type 'float': Invalid or lossy primitive coercion.");
assertFails(() -> nofloat.as(Float.class), ClassCastException.class,
"Cannot convert '1.7976931348623157E308'(language: Java, type: java.lang.Double) to Java type 'java.lang.Float': Invalid or lossy primitive coercion.");
Value nodouble = context.asValue(9007199254740993L);
assertFails(() -> nodouble.asDouble(), ClassCastException.class,
"Cannot convert '9007199254740993'(language: Java, type: java.lang.Long) to Java type 'double' using Value.asDouble(): Invalid or lossy primitive coercion. " +
"You can ensure that the value can be converted using Value.fitsInDouble().");
assertFails(() -> nodouble.as(double.class), ClassCastException.class,
"Cannot convert '9007199254740993'(language: Java, type: java.lang.Long) to Java type 'double': Invalid or lossy primitive coercion.");
assertFails(() -> nodouble.as(Double.class), ClassCastException.class,
"Cannot convert '9007199254740993'(language: Java, type: java.lang.Long) to Java type 'java.lang.Double': Invalid or lossy primitive coercion.");
Value noString = context.asValue(false);
assertFails(() -> noString.asString(), ClassCastException.class,
"Cannot convert 'false'(language: Java, type: java.lang.Boolean) to Java type 'java.lang.String' using Value.asString(): Invalid coercion. You can ensure that the value can be converted using Value.isString().");
assertFails(() -> noString.as(char.class), ClassCastException.class,
"Cannot convert 'false'(language: Java, type: java.lang.Boolean) to Java type 'char': Invalid or lossy primitive coercion.");
assertFails(() -> noString.as(String.class), ClassCastException.class,
"Cannot convert 'false'(language: Java, type: java.lang.Boolean) to Java type 'java.lang.String': Invalid or lossy primitive coercion.");
Value noBoolean = context.asValue("foobar");
assertFails(() -> noBoolean.asBoolean(), ClassCastException.class,
"Cannot convert 'foobar'(language: Java, type: java.lang.String) to Java type 'boolean' using Value.asBoolean(): Invalid or lossy primitive coercion. You can ensure that the value can be converted using Value.isBoolean().");
assertFails(() -> noBoolean.as(boolean.class), ClassCastException.class,
"Cannot convert 'foobar'(language: Java, type: java.lang.String) to Java type 'boolean': Invalid or lossy primitive coercion.");
assertFails(() -> noBoolean.as(Boolean.class), ClassCastException.class,
"Cannot convert 'foobar'(language: Java, type: java.lang.String) to Java type 'java.lang.Boolean': Invalid or lossy primitive coercion.");
}
private static class EmptyProxy implements org.graalvm.polyglot.proxy.Proxy {
@Override
public String toString() {
return "proxy";
}
}
@Test
public void testTypeCoercionError() {
Value pipe = context.asValue("not a pipe");
assertFails(() -> pipe.as(List.class), ClassCastException.class,
"Cannot convert 'not a pipe'(language: Java, type: java.lang.String) to Java type 'java.util.List': Unsupported target type.");
assertFails(() -> pipe.as(Map.class), ClassCastException.class,
"Cannot convert 'not a pipe'(language: Java, type: java.lang.String) to Java type 'java.util.Map': Unsupported target type.");
assertFails(() -> pipe.as(Function.class), ClassCastException.class,
"Cannot convert 'not a pipe'(language: Java, type: java.lang.String) to Java type 'java.util.function.Function': Unsupported target type.");
assertFails(() -> pipe.as(JavaInterface.class), ClassCastException.class,
"Cannot convert 'not a pipe'(language: Java, type: java.lang.String) to Java type 'com.oracle.truffle.api.test.polyglot.ValueAPITest$JavaInterface': Unsupported target type.");
assertFails(() -> pipe.as(PolyglotException.class), ClassCastException.class,
"Cannot convert 'not a pipe'(language: Java, type: java.lang.String) to Java type 'org.graalvm.polyglot.PolyglotException': Unsupported target type.");
Value other = context.asValue(new EmptyProxy());
assertFails(() -> other.as(List.class), ClassCastException.class,
"Cannot convert 'proxy'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$EmptyProxy) to Java type 'java.util.List': Value must have array elements.");
assertFails(() -> other.as(Map.class), ClassCastException.class,
"Cannot convert 'proxy'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$EmptyProxy) to Java type 'java.util.Map': Value must have members or array elements.");
assertFails(() -> other.as(Function.class), ClassCastException.class,
"Cannot convert 'proxy'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$EmptyProxy) to Java type 'java.util.function.Function': Value must be executable or instantiable.");
assertFails(() -> other.as(JavaInterface.class), ClassCastException.class,
"Cannot convert 'proxy'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$EmptyProxy) to Java type 'com.oracle.truffle.api.test.polyglot.ValueAPITest$JavaInterface': Value must have members.");
assertFails(() -> other.as(PolyglotException.class), ClassCastException.class,
"Cannot convert 'proxy'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$EmptyProxy) to Java type 'org.graalvm.polyglot.PolyglotException': Value must be an exception.");
}
@Test
public void testUnsupportedError() {
Value pipe = context.asValue("not a pipe");
assertFails(() -> pipe.getMember(""), UnsupportedOperationException.class,
"Unsupported operation Value.getMember(String) for 'not a pipe'(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasMembers().");
assertFails(() -> pipe.putMember("", null), UnsupportedOperationException.class,
"Unsupported operation Value.putMember(String, Object) for 'not a pipe'(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasMembers().");
assertFails(() -> pipe.getArrayElement(0), UnsupportedOperationException.class,
"Unsupported operation Value.getArrayElement(long) for 'not a pipe'(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
assertFails(() -> pipe.setArrayElement(0, null), UnsupportedOperationException.class,
"Unsupported operation Value.setArrayElement(long, Object) for 'not a pipe'(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
assertFails(() -> pipe.getArraySize(), UnsupportedOperationException.class,
"Unsupported operation Value.getArraySize() for 'not a pipe'(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
}
private static final TypeLiteral<List<String>> STRING_LIST = new TypeLiteral<List<String>>() {
};
private static final TypeLiteral<List<Integer>> INTEGER_LIST = new TypeLiteral<List<Integer>>() {
};
@Test
@SuppressWarnings("unchecked")
public void testArrayErrors() {
String[] array = new String[]{"asdf"};
Value v = context.asValue(array);
assertFails(() -> v.getArrayElement(1L), IndexOutOfBoundsException.class,
"Invalid array index 1 for array '[asdf]'(language: Java, type: java.lang.String[]).");
assertFails(() -> v.setArrayElement(1L, null), IndexOutOfBoundsException.class,
"Invalid array index 1 for array '[asdf]'(language: Java, type: java.lang.String[]).");
assertFails(() -> v.removeArrayElement(0), UnsupportedOperationException.class,
"Unsupported operation Value.removeArrayElement(long, Object) for '[asdf]'(language: Java, type: java.lang.String[]).");
assertFalse(v.removeMember("a"));
List<Object> list = v.as(List.class);
assertFails(() -> list.get(1), IndexOutOfBoundsException.class,
"Invalid index 1 for List<Object> '[asdf]'(language: Java, type: java.lang.String[]).");
assertFails(() -> list.set(1, null), IndexOutOfBoundsException.class,
"Invalid index 1 for List<Object> '[asdf]'(language: Java, type: java.lang.String[]).");
assertFails(() -> list.remove(0), UnsupportedOperationException.class,
"Unsupported operation remove for List<Object> '[asdf]'(language: Java, type: java.lang.String[]).");
List<?> stringList = v.as(STRING_LIST);
assertFails(() -> stringList.get(1), IndexOutOfBoundsException.class,
"Invalid index 1 for List<java.lang.String> '[asdf]'(language: Java, type: java.lang.String[]).");
assertFails(() -> stringList.set(1, null), IndexOutOfBoundsException.class,
"Invalid index 1 for List<java.lang.String> '[asdf]'(language: Java, type: java.lang.String[]).");
((List<Object>) stringList).set(0, "42");
assertEquals("42", stringList.get(0));
((List<Object>) stringList).set(0, context.asValue("42"));
assertEquals("42", stringList.get(0));
((List<Object>) stringList).set(0, context.asValue("foo"));
List<Integer> integerList = v.as(INTEGER_LIST);
assertFails(() -> integerList.get(0), ClassCastException.class,
"Cannot convert 'foo'(language: Java, type: java.lang.String) to Java type 'java.lang.Integer': Invalid or lossy primitive coercion.");
Value notAnArray = context.asValue("");
assertFails(() -> notAnArray.getArrayElement(0), UnsupportedOperationException.class,
"Unsupported operation Value.getArrayElement(long) for ''(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
assertFails(() -> notAnArray.setArrayElement(0, null), UnsupportedOperationException.class,
"Unsupported operation Value.setArrayElement(long, Object) for ''(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
assertFails(() -> notAnArray.getArraySize(), UnsupportedOperationException.class,
"Unsupported operation Value.getArraySize() for ''(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasArrayElements().");
Value rv = context.asValue(new ArrayList<>(Arrays.asList(new String[]{"a", "b", "c"})));
assertFails(() -> rv.removeArrayElement(3), IndexOutOfBoundsException.class,
"Invalid array index 3 for array '[a, b, c]'(language: Java, type: java.util.ArrayList).");
assertFalse(v.removeMember("a"));
}
private static final TypeLiteral<Map<String, String>> STRING_MAP = new TypeLiteral<Map<String, String>>() {
};
public static class MemberErrorTest {
public int value = 43;
public final int finalValue = 42;
@Override
public String toString() {
return "MemberErrorTest";
}
}
@Test
public void testMemberErrors() {
Value noMembers = context.asValue("");
assertFails(() -> noMembers.getMember(""), UnsupportedOperationException.class,
"Unsupported operation Value.getMember(String) for ''(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasMembers().");
assertFails(() -> noMembers.putMember("", null), UnsupportedOperationException.class,
"Unsupported operation Value.putMember(String, Object) for ''(language: Java, type: java.lang.String). You can ensure that the operation is supported using Value.hasMembers().");
assertFails(() -> noMembers.removeMember(""), UnsupportedOperationException.class,
"Unsupported operation Value.removeMember(String, Object) for ''(language: Java, type: java.lang.String).");
assertEquals(0, noMembers.getMemberKeys().size());
assertFalse(noMembers.hasMembers());
assertFalse(noMembers.hasMember(""));
assertFalse(noMembers.getMemberKeys().contains(""));
MemberErrorTest test = new MemberErrorTest();
Value v = context.asValue(test);
assertEquals(43, v.getMember("value").asInt());
v.putMember("value", 42);
assertEquals(42, v.getMember("value").asInt());
assertFails(() -> v.putMember("value", ""), IllegalArgumentException.class,
"Invalid member value ''(language: Java, type: java.lang.String) for object 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest) and member key 'value'.");
assertFails(() -> v.putMember("finalValue", 42), IllegalArgumentException.class,
"Invalid member key 'finalValue' for object 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
assertFails(() -> v.putMember("notAMember", ""), IllegalArgumentException.class,
"Invalid member key 'notAMember' for object 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
assertNull(v.getMember("notAMember"));
assertFalse(v.removeMember("notAMember"));
@SuppressWarnings("unchecked")
Map<Object, Object> map = v.as(Map.class);
assertNull(map.get("notAMember"));
map.put("value", 43);
assertEquals(43, map.get("value"));
assertFails(() -> map.put(new Object(), ""), IllegalArgumentException.class,
"Illegal identifier type 'java.lang.Object' for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
Map<String, String> stringMap = v.as(STRING_MAP);
assertFails(() -> stringMap.get("value"), ClassCastException.class,
"Cannot convert '43'(language: Java, type: java.lang.Integer) to Java type 'java.lang.String': Invalid or lossy primitive coercion.");
assertFails(() -> map.put("value", ""), ClassCastException.class,
"Invalid value ''(language: Java, type: java.lang.String) for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest) and identifier 'value'.");
assertFails(() -> map.put("value", "42"), ClassCastException.class,
"Invalid value '42'(language: Java, type: java.lang.String) for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest) and identifier 'value'.");
assertFails(() -> map.put("value", 4.2), ClassCastException.class,
"Invalid value '4.2'(language: Java, type: java.lang.Double) for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest) and identifier 'value'.");
assertFails(() -> map.put("finalValue", 42), IllegalArgumentException.class,
"Invalid or unmodifiable value for identifier 'finalValue' for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
assertFails(() -> map.put("finalValue", "42"), IllegalArgumentException.class,
"Invalid or unmodifiable value for identifier 'finalValue' for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
assertFails(() -> map.put("finalValue", 4.2), IllegalArgumentException.class,
"Invalid or unmodifiable value for identifier 'finalValue' for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
assertFails(() -> map.put("notAMember", ""), IllegalArgumentException.class,
"Invalid or unmodifiable value for identifier 'notAMember' for Map<Object, Object> 'MemberErrorTest'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$MemberErrorTest).");
Map<Object, Object> rmap = new HashMap<>();
rmap.put("value", 43);
Value rv = context.asValue(rmap);
assertFalse(rv.removeMember("notAMember"));
}
@Test
public void testNullError() {
Value members = context.asValue(new MembersAndArrayAndExecutableAndInstantiable());
assertFails(() -> members.putMember(null, "value"), NullPointerException.class, "identifier");
assertFails(() -> members.getMember(null), NullPointerException.class, "identifier");
assertFails(() -> members.hasMember(null), NullPointerException.class, "identifier");
assertFails(() -> members.removeMember(null), NullPointerException.class, "identifier");
assertFails(() -> members.execute((Object[]) null), NullPointerException.class, null);
assertFails(() -> members.executeVoid((Object[]) null), NullPointerException.class, null);
assertFails(() -> members.newInstance((Object[]) null), NullPointerException.class, null);
assertFails(() -> members.as((Class<?>) null), NullPointerException.class, null);
assertFails(() -> members.as((TypeLiteral<?>) null), NullPointerException.class, null);
}
@FunctionalInterface
public interface ExecutableInterface {
String execute(String argument);
}
@FunctionalInterface
public interface OtherInterface0 {
Object execute();
}
@FunctionalInterface
public interface OtherInterface1 {
Object execute(Object s);
}
@FunctionalInterface
public interface OtherInterface2 {
Object execute(String s, String s2);
}
@SuppressWarnings("unused")
public static class AmbiguousType {
public String f(int a, byte b) {
return "1";
}
public String f(byte a, int b) {
return "2";
}
}
@Test
public void testExecutableErrors() {
ExecutableInterface executable = new ExecutableInterface() {
public String execute(String argument) {
return argument;
}
@Override
public String toString() {
return "testExecutable";
}
};
Value v = context.asValue(executable);
assertTrue(v.canExecute());
assertEquals("", v.execute("").as(Object.class));
assertEquals("", v.execute("").asString());
String className = executable.getClass().getName();
assertFails(() -> v.execute("", ""), IllegalArgumentException.class,
"Invalid argument count when executing 'testExecutable'(language: Java, type: " + className + ") " +
"with arguments [''(language: Java, type: java.lang.String), ''(language: Java, type: java.lang.String)]. Expected 1 argument(s) but got 2.");
assertFails(() -> v.execute(), IllegalArgumentException.class,
"Invalid argument count when executing 'testExecutable'(language: Java, type: " + className + ") with arguments []." +
" Expected 1 argument(s) but got 0.");
assertTrue(v.execute("42").isString());
assertEquals("42", v.execute("42").asString());
assertFails(() -> context.asValue("").execute(), UnsupportedOperationException.class,
"Unsupported operation Value.execute(Object...) for ''(language: Java, type: java.lang.String). You can ensure that the operation " +
"is supported using Value.canExecute().");
assertFails(() -> v.as(OtherInterface0.class).execute(), IllegalArgumentException.class,
"Invalid argument count when executing 'testExecutable'(language: Java, type: " + className + ") " +
"with arguments []. Expected 1 argument(s) but got 0.");
assertEquals("", v.as(OtherInterface1.class).execute(""));
assertEquals("42", v.as(OtherInterface1.class).execute("42"));
assertFails(() -> v.as(OtherInterface2.class).execute("", ""), IllegalArgumentException.class,
"Invalid argument count when executing 'testExecutable'(language: Java, " +
"type: " + className + ") with arguments [''(language: Java, type: java.lang.String), " +
"''(language: Java, type: java.lang.String)]. Expected 1 argument(s) but got 2.");
assertSame(executable, v.as(ExecutableInterface.class));
Value value = context.asValue(new AmbiguousType());
assertFails(() -> value.getMember("f").execute(1, 2), IllegalArgumentException.class,
"Invalid argument when executing 'com.oracle.truffle.api.test.polyglot.ValueAPITest$AmbiguousType." +
"f'(language: Java, type: Unknown). Multiple applicable overloads found for method name f " +
"(candidates: [Method[public java.lang.String com.oracle.truffle.api.test.polyglot.ValueAPITest$AmbiguousType." +
"f(int,byte)], Method[public java.lang.String com.oracle.truffle.api.test.polyglot.ValueAPITest$AmbiguousType." +
"f(byte,int)]], arguments: [1 (Integer), 2 (Integer)]) Provided arguments: " +
"['1'(language: Java, type: java.lang.Integer), '2'(language: Java, type: java.lang.Integer)].");
}
public static class InvocableType {
@SuppressWarnings("unused")
public String f(int a, byte b) {
return "1";
}
@Override
public String toString() {
return getClass().getCanonicalName();
}
}
@Test
public void testInvokableErrors() {
Value value = context.asValue(new InvocableType());
assertTrue(value.canInvokeMember("f"));
assertFails(() -> value.invokeMember(""), IllegalArgumentException.class,
"Invalid member key '' for object 'com.oracle.truffle.api.test.polyglot.ValueAPITest.InvocableType'" +
"(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$InvocableType).");
assertFails(() -> value.invokeMember("f", 2), IllegalArgumentException.class,
"Invalid argument count when invoking 'f' on 'com.oracle.truffle.api.test.polyglot.ValueAPITest.InvocableType'" +
"(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$InvocableType) " +
"with arguments ['2'(language: Java, type: java.lang.Integer)]. Expected 2 argument(s) but got 1.");
assertFails(() -> value.invokeMember("f", "2", "128"), IllegalArgumentException.class,
"Invalid argument when invoking 'f' on 'com.oracle.truffle.api.test.polyglot.ValueAPITest." +
"InvocableType'(language: Java, type: com.oracle.truffle.api.test.polyglot.ValueAPITest$InvocableType). " +
"Cannot convert '2'(language: Java, type: java.lang.String) to Java type 'int': Invalid or lossy primitive coercion." +
"Provided arguments: ['2'(language: Java, type: java.lang.String), '128'(language: Java, type: java.lang.String)].");
assertEquals("1", value.invokeMember("f", 2, 3).asString());
Value primitiveValue = context.asValue(42);
assertFails(() -> primitiveValue.invokeMember(""), UnsupportedOperationException.class,
"Unsupported operation Value.invoke(, Object...) for '42'(language: Java, type: java.lang.Integer)." +
" You can ensure that the operation is supported using Value.canInvoke(String).");
}
private static void assertFails(Runnable r, Class<?> hostExceptionType, String message) {
try {
r.run();
Assert.fail("No error but expected " + hostExceptionType);
} catch (Exception e) {
if (!hostExceptionType.isInstance(e)) {
throw new AssertionError(e.getClass().getName() + ":" + e.getMessage(), e);
}
if (message != null && !message.equals(e.getMessage())) {
ComparisonFailure f = new ComparisonFailure(null, message, e.getMessage());
f.initCause(e);
throw f;
}
}
}
@Test
public void testList() {
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
Value v = context.asValue(list);
assertTrue(v.hasArrayElements());
assertTrue(v.hasMembers());
assertEquals("foo", v.getArrayElement(0).asString());
assertEquals("bar", v.getArrayElement(1).asString());
AbstractPolyglotTest.assertFails(() -> v.getArrayElement(2), ArrayIndexOutOfBoundsException.class);
v.setArrayElement(2, "baz");
assertEquals("foo", v.getArrayElement(0).asString());
assertEquals("bar", v.getArrayElement(1).asString());
assertEquals("baz", v.getArrayElement(2).asString());
assertTrue(v.removeArrayElement(1));
assertEquals("foo", v.getArrayElement(0).asString());
assertEquals("baz", v.getArrayElement(1).asString());
assertTrue(v.removeArrayElement(0));
assertEquals("baz", v.getArrayElement(0).asString());
assertTrue(v.removeArrayElement(0));
assertTrue(v.getArraySize() == 0);
}
@Test
public void testRecursiveList() {
Object[] o1 = new Object[1];
Object[] o2 = new Object[]{o1};
o1[0] = o2;
Value v1 = context.asValue(o1);
Value v2 = context.asValue(o2);
assertEquals(v1.as(List.class), v1.as(List.class));
assertEquals(v2.as(List.class), v2.as(List.class));
assertNotEquals(v1.as(List.class), (v2.as(List.class)));
assertNotEquals(v1, v2);
assertEquals(v1, v1);
assertEquals(v2, v2);
ValueAssert.assertValue(v1);
ValueAssert.assertValue(v2);
}
public static class RecursiveObject {
@HostAccess.Export public RecursiveObject rec;
}
@Test
public void testRecursiveObject() {
RecursiveObject o1 = new RecursiveObject();
RecursiveObject o2 = new RecursiveObject();
o1.rec = o2;
o2.rec = o1;
Value v1 = context.asValue(o1);
Value v2 = context.asValue(o2);
assertEquals(v1.as(Map.class), v1.as(Map.class));
assertEquals(v2.as(Map.class), v2.as(Map.class));
assertNotEquals(v1.as(Map.class), v2.as(Map.class));
assertNotEquals(v1, v2);
assertEquals(v1, v1);
assertEquals(v2, v2);
ValueAssert.assertValue(v1);
ValueAssert.assertValue(v2);
}
@Implementable
public interface EmptyInterface {
void foo();
void bar();
}
@FunctionalInterface
public interface EmptyFunctionalInterface {
void noop();
}
@ExportLibrary(InteropLibrary.class)
@SuppressWarnings({"static-method", "unused"})
static final class TestObject implements TruffleObject {
@ExportMessage
boolean hasMembers() {
return true;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
boolean isExecutable() {
return true;
}
@ExportMessage
Object execute(Object[] arguments) throws UnsupportedMessageException {
throw UnsupportedMessageException.create();
}
@ExportMessage
Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
throw UnsupportedMessageException.create();
}
@ExportMessage
Object readArrayElement(long index) throws InvalidArrayIndexException {
throw InvalidArrayIndexException.create(index);
}
@ExportMessage
long getArraySize() {
return 0L;
}
@ExportMessage
boolean isArrayElementReadable(long index) {
return false;
}
}
@Test
public void testValueContextPropagation() {
Object o = new TestObject();
ProxyLanguage.setDelegate(new ProxyLanguage() {
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(o));
}
@Override
protected boolean isObjectOfLanguage(Object object) {
return object == o;
}
@Override
protected String toString(@SuppressWarnings("hiding") LanguageContext context, Object value) {
if (o == value) {
return "true";
} else {
return "false";
}
}
});
Value v = context.eval(ProxyLanguage.ID, "");
assertEquals("true", v.toString());
assertEquals("true", context.asValue(v).toString());
assertEquals("true", v.as(Map.class).toString());
assertEquals("true", v.as(Function.class).toString());
assertEquals("true", v.as(List.class).toString());
assertEquals("true", context.asValue(v.as(Map.class)).toString());
assertEquals("true", context.asValue(v.as(Function.class)).toString());
assertEquals("true", context.asValue(v.as(List.class)).toString());
assertEquals(v, v);
assertEquals(v, context.asValue(v));
assertEquals(v.as(Map.class), v.as(Map.class));
assertEquals(v.as(Function.class), v.as(Function.class));
assertEquals(v.as(List.class), v.as(List.class));
assertEquals(v.as(Map.class), context.asValue(v.as(Map.class)).as(Map.class));
assertEquals(v.as(Function.class), context.asValue(v.as(Function.class)).as(Function.class));
assertEquals(v.as(List.class), context.asValue(v.as(List.class)).as(List.class));
assertNotEquals(v.as(Function.class), v.as(Map.class));
assertNotEquals(v.as(Function.class), v.as(List.class));
assertNotEquals(v.as(Map.class), v.as(Function.class));
assertNotEquals(v.as(Map.class), v.as(List.class));
assertNotEquals(v.as(List.class), v.as(Function.class));
assertNotEquals(v.as(List.class), v.as(Map.class));
}
@Test
public void testAsValue() {
for (Object number : NUMBERS) {
ValueAssert.assertValue(Value.asValue(number), Trait.NUMBER);
}
for (Object string : STRINGS) {
ValueAssert.assertValue(Value.asValue(string), Trait.STRING);
}
for (Object b : BOOLEANS) {
ValueAssert.assertValue(Value.asValue(b), Trait.BOOLEAN);
}
for (Object b : HOST_OBJECTS) {
Value v = Value.asValue(b);
assertTrue(v.isHostObject());
ValueAssert.assertValue(v);
}
Object o = new Object();
Value v = Value.asValue(o);
ProxyExecutable executable = new ProxyExecutable() {
public Object execute(Value... arguments) {
return arguments[0].invokeMember("hashCode");
}
};
assertEquals(o.hashCode(), context.asValue(executable).execute(v).asInt());
Function<Object, Object> f = (a) -> Value.asValue(a).invokeMember("hashCode");
assertEquals(o.hashCode(), context.asValue(f).execute(v).asInt());
assertTrue(Value.asValue(null).isNull());
assertNull(Value.asValue(null).as(Object.class));
assertNull(Value.asValue(null).as(String.class));
assertNull(Value.asValue(null).as(Integer.class));
ProxyExecutable executableProxy = new ProxyExecutable() {
public Object execute(Value... arguments) {
return null;
}
};
assertTrue(Value.asValue(executableProxy).isProxyObject());
assertSame(executableProxy, Value.asValue(executableProxy).asProxyObject());
assertFalse(Value.asValue(executableProxy).isHostObject());
assertFalse(Value.asValue(executableProxy).canExecute());
Object hostWrapper = context.asValue(executableProxy).as(Function.class);
assertTrue(Value.asValue(hostWrapper).canExecute());
ProxyObject objectProxy = ProxyObject.fromMap(new HashMap<>());
hostWrapper = context.asValue(objectProxy).as(Map.class);
assertTrue(Value.asValue(hostWrapper).hasMembers());
ProxyArray arrayProxy = ProxyArray.fromArray("");
hostWrapper = context.asValue(arrayProxy).as(List.class);
assertTrue(Value.asValue(hostWrapper).hasArrayElements());
assertTrue(context.asValue(Value.asValue(executableProxy)).canExecute());
context.asValue(new ProxyExecutable() {
public Object execute(Value... arguments) {
assertTrue(arguments[0].canExecute());
return null;
}
}).execute(Value.asValue(executableProxy));
}
@Test
public void testHostObjectsAndPrimitivesNonSharable() {
Context context1 = Context.create();
Context context2 = Context.create();
List<Object> nonSharables = new ArrayList<>();
Object interopObject = new TestObject();
nonSharables.add(interopObject);
Value v = context1.asValue(interopObject);
nonSharables.add(v.as(Map.class));
nonSharables.add(v.as(EmptyInterface.class));
nonSharables.add(v.as(List.class));
nonSharables.add(v.as(Function.class));
nonSharables.add(v.as(EmptyFunctionalInterface.class));
nonSharables.add(v);
nonSharables.add(Value.asValue(v));
for (Object object : nonSharables) {
Object nonSharableObject = object;
if (nonSharableObject instanceof TruffleObject) {
nonSharableObject = context1.asValue(nonSharableObject);
}
try {
context2.getPolyglotBindings().putMember("foo", nonSharableObject);
fail();
} catch (PolyglotException e) {
assertTrue(e.getMessage(), e.getMessage().contains("cannot be passed from one context to another"));
}
ProxyExecutable executable = new ProxyExecutable() {
public Object execute(Value... arguments) {
return 42;
}
};
assertEquals(42, context1.asValue(executable).execute(nonSharableObject).asInt());
try {
context2.asValue(executable).execute(nonSharableObject);
fail();
} catch (PolyglotException e) {
assertTrue(e.getMessage(), e.getMessage().contains("cannot be passed from one context to another"));
}
nonSharableObject.toString();
assertTrue(nonSharableObject.equals(nonSharableObject));
assertTrue(nonSharableObject.hashCode() == nonSharableObject.hashCode());
}
context1.close();
context2.close();
}
@Test
public void testHostObjectsAndPrimitivesSharable() {
Context context1 = Context.newBuilder().allowHostAccess(HostAccess.ALL).build();
Context context2 = Context.create();
List<Object> sharableObjects = new ArrayList<>();
sharableObjects.addAll(Arrays.asList(HOST_OBJECTS));
sharableObjects.addAll(Arrays.asList(NUMBERS));
sharableObjects.addAll(Arrays.asList(BOOLEANS));
sharableObjects.addAll(Arrays.asList(STRINGS));
sharableObjects.addAll(Arrays.asList(ARRAYS));
expandObjectVariants(context1, sharableObjects);
for (Object object : sharableObjects) {
List<Object> variants = new ArrayList<>();
variants.add(context1.asValue(object));
variants.add(object);
variants.add(Value.asValue(object));
ProxyExecutable executable = new ProxyExecutable() {
public Object execute(Value... arguments) {
return 42;
}
};
for (Object variant : sharableObjects) {
context2.getPolyglotBindings().putMember("foo", variant);
assertEquals(42, context2.asValue(executable).execute(variant).asInt());
}
object.toString();
assertTrue(object.equals(object));
assertTrue(object.hashCode() == object.hashCode());
}
Value contextLessValue = Value.asValue(new TestObject());
context1.getPolyglotBindings().putMember("foo", contextLessValue);
context2.getPolyglotBindings().putMember("foo", contextLessValue);
context1.close();
context2.close();
}
private static void expandObjectVariants(Context sourceContext, List<Object> objects) {
for (Object object : objects.toArray()) {
Value v = sourceContext.asValue(object);
if (v.hasMembers()) {
objects.add(v.as(Map.class));
objects.add(v.as(EmptyInterface.class));
}
if (v.hasArrayElements()) {
objects.add(v.as(List.class));
objects.add(v.as(Object[].class));
}
if (v.canExecute()) {
objects.add(v.as(Function.class));
objects.add(v.as(EmptyFunctionalInterface.class));
}
objects.add(v);
objects.add(Value.asValue(v));
objects.add(Value.asValue(object));
}
}
@Test
public void testHostException() {
Value exceptionValue = context.asValue(new RuntimeException("expected"));
assertValue(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION);
try {
exceptionValue.throwException();
fail("should have thrown");
} catch (PolyglotException expected) {
assertThat(expected.getMessage(), containsString("expected"));
assertTrue("expected a host exception", expected.isHostException());
} catch (UnsupportedOperationException unsupported) {
throw new AssertionError(unsupported);
}
PolyglotException polyglotException = exceptionValue.as(PolyglotException.class);
assertNotNull(polyglotException);
assertThat(polyglotException.getMessage(), containsString("expected"));
}
@Test
public void testGuestException() {
Value exceptionValue = context.asValue(new ExceptionWrapper(new LanguageException("expected")));
assertValue(exceptionValue, EXCEPTION);
try {
exceptionValue.throwException();
fail("should have thrown");
} catch (PolyglotException expected) {
assertThat(expected.getMessage(), containsString("expected"));
assertTrue("expected a guest exception", expected.isGuestException());
} catch (UnsupportedOperationException unsupported) {
throw new AssertionError(unsupported);
}
PolyglotException polyglotException = exceptionValue.as(PolyglotException.class);
assertNotNull(polyglotException);
assertThat(polyglotException.getMessage(), containsString("expected"));
}
@Test
public void testMetaObject() {
Value v = context.asValue(OtherInterface0.class);
assertTrue(v.isMetaObject());
assertEquals(OtherInterface0.class.getTypeName(), v.getMetaQualifiedName());
assertEquals(OtherInterface0.class.getSimpleName(), v.getMetaSimpleName());
assertTrue(v.isMetaInstance(new OtherInterface0() {
@Override
public Object execute() {
return null;
}
}));
assertFalse(v.isMetaInstance(new OtherInterface1() {
@Override
public Object execute(Object s) {
return null;
}
}));
}
@Test
public void testMetaObjectNull() {
Value nullValue = context.asValue(null);
assertFalse(context.asValue(nullValue).isMetaObject());
assertNull(context.asValue(nullValue).getMetaObject());
assertFalse(context.asValue(null).isMetaObject());
assertNull(context.asValue(null).getMetaObject());
}
@Test
public void testIsMetaInstanceNull() {
Value nullValue = context.asValue(null);
assertFalse(context.asValue(Object.class).isMetaInstance(nullValue));
assertFalse(context.asValue(Void.class).isMetaInstance(nullValue));
assertFalse(context.asValue(Object.class).isMetaInstance(null));
assertFalse(context.asValue(Void.class).isMetaInstance(null));
}
@ExportLibrary(InteropLibrary.class)
static final class StringWrapper implements TruffleObject {
final String string;
StringWrapper(String string) {
this.string = string;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isString() {
return true;
}
@ExportMessage
String asString() {
return string;
}
}
static final InteropLibrary INTEROP = InteropLibrary.getFactory().getUncached();
@ExportLibrary(value = InteropLibrary.class, delegateTo = "number")
static final class NumberWrapper implements TruffleObject {
final Number number;
NumberWrapper(Number number) {
this.number = number;
}
}
@ExportLibrary(InteropLibrary.class)
static final class BooleanWrapper implements TruffleObject {
final boolean delegate;
BooleanWrapper(boolean delegate) {
this.delegate = delegate;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isBoolean() {
return true;
}
@ExportMessage
boolean asBoolean() {
return delegate;
}
}
@ExportLibrary(InteropLibrary.class)
static final class ExceptionWrapper implements TruffleObject {
final RuntimeException delegate;
ExceptionWrapper(RuntimeException delegate) {
this.delegate = delegate;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isException() {
return true;
}
@ExportMessage
RuntimeException throwException() {
throw delegate;
}
}
@ExportLibrary(value = InteropLibrary.class, delegateTo = "delegate")
static final class BooleanAndDelegate implements TruffleObject {
final Object delegate;
boolean booleanValue;
BooleanAndDelegate(Object delegate) {
this.delegate = delegate;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isBoolean() {
return true;
}
@ExportMessage
boolean asBoolean() {
return booleanValue;
}
}
@ExportLibrary(InteropLibrary.class)
static final class TestArray implements TruffleObject {
@CompilationFinal(dimensions = 1) private final String[] members;
TestArray(String[] members) {
this.members = members;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return members.length;
}
@ExportMessage
boolean isArrayElementReadable(long idx) {
return 0 <= idx && idx < members.length;
}
@ExportMessage
String readArrayElement(long idx,
@Cached BranchProfile exception) throws InvalidArrayIndexException {
if (!isArrayElementReadable(idx)) {
exception.enter();
throw InvalidArrayIndexException.create(idx);
}
return members[(int) idx];
}
}
@SuppressWarnings("serial")
private static final class LanguageException extends AbstractTruffleException {
LanguageException(String message) {
super(message);
}
}
@Test
public void testPrimitiveAndObject() {
BooleanAndDelegate o = new BooleanAndDelegate(new TestArray(new String[0]));
Value v = context.asValue(o);
ValueAssert.assertValue(v, Trait.ARRAY_ELEMENTS, Trait.BOOLEAN);
}
@Test
public void testBadNarrowingConversions() {
final long[] badIndices = {-1L << 32, -1L << 32 + 1, 1L << 32, 1L << 32 + 1, Long.MIN_VALUE, Long.MAX_VALUE};
for (long index : badIndices) {
Value list = context.asValue(new ArrayList<>(Arrays.asList(1, 2, 3)));
AbstractPolyglotTest.assertFails(() -> list.getArrayElement(index), ArrayIndexOutOfBoundsException.class);
AbstractPolyglotTest.assertFails(() -> list.setArrayElement(index, 42), ArrayIndexOutOfBoundsException.class);
AbstractPolyglotTest.assertFails(() -> list.removeArrayElement(index), ArrayIndexOutOfBoundsException.class);
Value array = context.asValue(new int[]{1, 2, 3});
AbstractPolyglotTest.assertFails(() -> array.getArrayElement(index), ArrayIndexOutOfBoundsException.class);
AbstractPolyglotTest.assertFails(() -> array.setArrayElement(index, 42), ArrayIndexOutOfBoundsException.class);
}
}
}