package com.oracle.truffle.api.test.polyglot;
import static com.oracle.truffle.api.test.polyglot.AbstractPolyglotTest.assertFails;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.HostAccess.Export;
import org.graalvm.polyglot.HostAccess.Implementable;
import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence;
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.ProxyExecutable;
import org.graalvm.polyglot.proxy.ProxyObject;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import com.oracle.truffle.tck.tests.ValueAssert;
import com.oracle.truffle.tck.tests.ValueAssert.Trait;
public class HostAccessTest {
public static class OK {
public int value = 42;
}
public static class Ban {
public int value = 24;
}
@Test
public void usefulToStringExplicit() {
assertEquals("HostAccess.EXPLICIT", HostAccess.EXPLICIT.toString());
}
@Test
public void usefulToStringPublic() {
assertEquals("HostAccess.ALL", HostAccess.ALL.toString());
}
@Test
public void usefulToStringNone() {
assertEquals("HostAccess.NONE", HostAccess.NONE.toString());
}
@Test
public void constantsCanBeCopied() {
verifyObjectImpl(HostAccess.NONE);
verifyObjectImpl(HostAccess.EXPLICIT);
verifyObjectImpl(HostAccess.ALL);
}
private static void verifyObjectImpl(HostAccess access) {
HostAccess otherAccess = HostAccess.newBuilder(access).build();
assertNotSame(access, otherAccess);
assertEquals(access, otherAccess);
assertEquals(access.hashCode(), otherAccess.hashCode());
assertNotNull(access.toString());
}
public static class MyEquals {
@Override
public boolean equals(Object arg0) {
return arg0 != null && getClass() == arg0.getClass();
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
@Test
public void banAccessToReflection() throws Exception {
HostAccess config = HostAccess.newBuilder().
allowPublicAccess(true).
denyAccess(Class.class).
denyAccess(Method.class).
denyAccess(Field.class).
denyAccess(Proxy.class).
denyAccess(Object.class, false).
build();
setupEnv(config);
Value readValue = context.eval("sl", "" +
"function readValue(x, y) {\n" +
" return x.equals(y);\n" +
"}\n" +
"function main() {\n" +
" return readValue;\n" +
"}\n");
MyEquals myEquals = new MyEquals();
assertTrue("MyEquals.equals method is accessible", readValue.execute(myEquals, myEquals).asBoolean());
Value res;
try {
res = readValue.execute(new Object());
} catch (PolyglotException ex) {
return;
}
fail("expecting no result: " + res);
}
@Test
public void banAccessToEquals() throws Exception {
HostAccess config = HostAccess.newBuilder().
allowPublicAccess(true).
denyAccess(Object.class, false).
build();
setupEnv(config);
Value readValue = context.eval("sl", "" +
"function readValue(x, y) {\n" +
" return x.equals(y);\n" +
"}\n" +
"function main() {\n" +
" return readValue;\n" +
"}\n");
MyEquals myEquals = new MyEquals();
assertTrue("MyEquals.equals method is accessible", readValue.execute(myEquals, myEquals).asBoolean());
Value res;
try {
res = readValue.execute(new Object());
} catch (PolyglotException ex) {
return;
}
fail("expecting no result: " + res);
}
@Test
public void banAccessToLoadClass() throws Exception {
HostAccess config = HostAccess.newBuilder().
allowPublicAccess(true).
denyAccess(ClassLoader.class).
build();
setupEnv(config);
Value loadClass = context.eval("sl", "" +
"function loadClass(x) {\n" +
" return x.loadClass(\"java.lang.String\");\n" +
"}\n" +
"function main() {\n" +
" return loadClass;\n" +
"}\n");
URLClassLoader loader = new URLClassLoader(new URL[0]);
Value res;
try {
res = loadClass.execute(loader);
} catch (PolyglotException ex) {
return;
}
fail("expecting no result: " + res);
}
@Test
public void banAccessToOverwrittenLoadClass() throws Exception {
HostAccess config = HostAccess.newBuilder().
allowPublicAccess(true).
denyAccess(ClassLoader.class).
build();
setupEnv(config);
Value loadClass = context.eval("sl", "" +
"function loadClass(x) {\n" +
" return x.loadClass(\"java.lang.String\");\n" +
"}\n" +
"function main() {\n" +
" return loadClass;\n" +
"}\n");
URLClassLoader loader = new MyClassLoader(new URL[0]);
Value res;
try {
res = loadClass.execute(loader);
} catch (PolyglotException ex) {
return;
}
fail("expecting no result: " + res);
}
@Test
public void publicCanAccessObjectEquals() throws Exception {
setupEnv(HostAccess.ALL);
Value readValue = context.eval("sl", "" +
"function readValue(x, y) {\n" +
" return x.equals(y);\n" +
"}\n" +
"function main() {\n" +
" return readValue;\n" +
"}\n");
assertFalse("Cannot read equals 1", readValue.execute(new Object(), new Object()).asBoolean());
Object same = new Object();
assertTrue("Cannot read equals 2", readValue.execute(same, same).asBoolean());
}
@Test
public void inheritFromPublic() throws Exception {
setupEnv(HostAccess.newBuilder().allowPublicAccess(true));
Value readValue = context.eval("sl", "" +
"function readValue(x, y) {\n" +
" return x.equals(y);\n" +
"}\n" +
"function main() {\n" +
" return readValue;\n" +
"}\n");
assertFalse("Cannot read equals 1", readValue.execute(new Object(), new Object()).asBoolean());
Object same = new Object();
assertTrue("Cannot read equals 2", readValue.execute(same, same).asBoolean());
}
@Test
public void useOneHostAccessByTwoContexts() throws Exception {
HostAccess config = HostAccess.newBuilder().allowAccess(OK.class.getField("value")).build();
try (
Context c1 = Context.newBuilder().allowHostAccess(config).build();
Context c2 = Context.newBuilder().allowHostAccess(config).build()) {
assertAccess(c1);
assertAccess(c2);
}
}
private static void assertAccess(Context context) {
Value readValue = context.eval("sl", "" +
"function readValue(x) {\n" +
" return x.value;\n" +
"}\n" +
"function main() {\n" +
" return readValue;\n" +
"}\n");
assertEquals(42, readValue.execute(new OK()).asInt());
ExposeToGuestTest.assertPropertyUndefined("public isn't enough by default", "value", readValue, new Ban());
}
@Test
public void onlyOneHostAccessPerEngine() throws Exception {
Engine shared = Engine.create();
HostAccess config = HostAccess.newBuilder().allowAccess(OK.class.getField("value")).build();
Context c1 = Context.newBuilder().engine(shared).allowHostAccess(config).build();
Context.Builder builder = Context.newBuilder().engine(shared).allowHostAccess(HostAccess.ALL);
try {
builder.build();
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage(), ex.getMessage().startsWith("Found different host access configuration for a context with a shared engine."));
}
c1.close();
}
@Test
public void testArrayAccessEnabled() {
setupEnv(HostAccess.newBuilder().allowArrayAccess(true));
int[] array = new int[]{1, 2, 3};
Value value = context.asValue(array);
assertTrue(value.hasArrayElements());
assertEquals(3, value.getArraySize());
assertEquals(1, value.getArrayElement(0).asInt());
assertEquals(2, value.getArrayElement(1).asInt());
assertEquals(3, value.getArrayElement(2).asInt());
value.setArrayElement(2, 42);
assertEquals(42, value.getArrayElement(2).asInt());
assertEquals(42, array[2]);
assertSame(array, value.asHostObject());
array[2] = 43;
assertEquals(43, value.getArrayElement(2).asInt());
try {
value.removeArrayElement(2);
fail();
} catch (UnsupportedOperationException e) {
}
assertEquals(0, value.getMemberKeys().size());
ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT);
}
@Test
public void testArrayAccessDisabled() {
setupEnv(HostAccess.newBuilder().allowArrayAccess(false));
assertArrayAccessDisabled(context);
}
@Test
public void testPublicAccessNoArrayAccess() {
setupEnv(HostAccess.newBuilder().allowPublicAccess(true));
assertArrayAccessDisabled(context);
}
private static void assertArrayAccessDisabled(Context context) {
int[] array = new int[]{1, 2, 3};
Value value = context.asValue(array);
assertSame(array, value.asHostObject());
ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT);
}
@Test
public void testListAccessEnabled() {
setupEnv(HostAccess.newBuilder().allowListAccess(true));
List<Integer> array = new ArrayList<>(Arrays.asList(1, 2, 3));
Value value = context.asValue(array);
assertTrue(value.hasArrayElements());
assertEquals(3, value.getArraySize());
assertEquals(1, value.getArrayElement(0).asInt());
assertEquals(2, value.getArrayElement(1).asInt());
assertEquals(3, value.getArrayElement(2).asInt());
value.setArrayElement(2, 42);
assertEquals(42, value.getArrayElement(2).asInt());
assertEquals(42, (int) array.get(2));
array.set(2, 43);
assertEquals(43, value.getArrayElement(2).asInt());
value.removeArrayElement(2);
assertEquals(2, value.getArraySize());
assertSame(array, value.asHostObject());
assertEquals(0, value.getMemberKeys().size());
ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT);
assertArrayAccessDisabled(context);
}
@Test
public void testListAccessDisabled() {
setupEnv(HostAccess.newBuilder().allowListAccess(false));
assertListAccessDisabled(context);
}
@Test
public void testPublicAccessNoListAccess() {
setupEnv(HostAccess.newBuilder().allowPublicAccess(true).allowListAccess(false));
assertListAccessDisabled(context);
}
private static void assertListAccessDisabled(Context context) {
List<Integer> array = new ArrayList<>(Arrays.asList(1, 2, 3));
Value value = context.asValue(array);
assertSame(array, value.asHostObject());
ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT);
}
private Context context;
private void setupEnv(HostAccess.Builder builder) {
tearDown();
if (builder != null) {
builder.allowImplementationsAnnotatedBy(FunctionalInterface.class);
builder.allowImplementationsAnnotatedBy(HostAccess.Implementable.class);
builder.allowAccessAnnotatedBy(HostAccess.Export.class);
HostAccess access = builder.build();
verifyObjectImpl(access);
setupEnv(access);
}
}
private void setupEnv(HostAccess access) {
tearDown();
verifyObjectImpl(access);
context = Context.newBuilder().allowHostAccess(access).build();
}
@After
public void tearDown() {
if (context != null) {
context.close();
context = null;
}
}
public static final class TargetClass1 {
private final Object o;
TargetClass1(Object o) {
this.o = o;
}
}
public static final class TargetClass2 {
TargetClass2(@SuppressWarnings("unused") Object o) {
}
}
@SuppressWarnings("static-method")
public static final class MultiMethod1 {
@Export
public TargetClass1 m(TargetClass1 arg0) {
return arg0;
}
@Export
public TargetClass2 m(TargetClass2 arg0) {
return arg0;
}
}
@SuppressWarnings("static-method")
public static final class MultiMethod2 {
@Export
public Object m(@SuppressWarnings("unused") TargetClass1 arg0, Object arg1) {
return arg1;
}
@Export
public String m(@SuppressWarnings("unused") TargetClass1 arg0, String arg1) {
return arg1;
}
}
@SuppressWarnings("static-method")
public static final class SingleMethod {
@Export
public TargetClass1 m(TargetClass1 arg0) {
return arg0;
}
}
@Test
public void testConverterOverloads() {
HostAccess.Builder builder;
Value methods;
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
methods = context.asValue(new SingleMethod());
assertEquals(TargetClass1.class, methods.invokeMember("m", "42").asHostObject().getClass());
try {
methods.invokeMember("m", "43");
fail();
} catch (IllegalArgumentException e) {
}
TargetClass1 o = methods.invokeMember("m", "42").asHostObject();
assertEquals("42", o.o);
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass2.class, (v) -> v.equals("43"), (v) -> new TargetClass2(v));
setupEnv(builder);
methods = context.asValue(new MultiMethod1());
assertEquals(TargetClass1.class, methods.invokeMember("m", "42").asHostObject().getClass());
assertEquals(TargetClass2.class, methods.invokeMember("m", "43").asHostObject().getClass());
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass2.class, (v) -> v.equals("42"), (v) -> new TargetClass2(v));
setupEnv(builder);
methods = context.asValue(new MultiMethod1());
try {
methods.invokeMember("m", "42");
fail();
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage(), e.getMessage().contains("Multiple applicable overloads found"));
}
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, null, (v) -> new TargetClass1(v));
setupEnv(builder);
methods = context.asValue(new MultiMethod2());
assertEquals("42", methods.invokeMember("m", "42", "42").asString());
assertEquals(42, methods.invokeMember("m", "42", 42).asInt());
}
@Test
public void testConverterArray() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
TargetClass1[] array = context.asValue(new String[]{"42", "42"}).as(TargetClass1[].class);
assertEquals(2, array.length);
assertEquals("42", array[0].o);
assertEquals("42", array[1].o);
try {
context.asValue(new String[]{"42", "43"}).as(TargetClass1[].class);
fail();
} catch (ClassCastException e) {
assertTrue(e.getMessage(), e.getMessage().startsWith("Cannot convert '43'"));
}
builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.startsWith("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
array = context.asValue(new String[]{"42", "422"}).as(TargetClass1[].class);
assertEquals(2, array.length);
assertEquals("42", array[0].o);
assertEquals("422", array[1].o);
}
static final TypeLiteral<List<TargetClass1>> TARGET_CLASS_LIST = new TypeLiteral<List<TargetClass1>>() {
};
@Test
public void testConverterList() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
List<TargetClass1> list = context.asValue(new String[]{"42", "42"}).as(TARGET_CLASS_LIST);
assertEquals(2, list.size());
assertEquals("42", list.get(0).o);
assertEquals("42", list.get(1).o);
list = context.asValue(new String[]{"42", "43"}).as(TARGET_CLASS_LIST);
try {
list.get(1);
fail();
} catch (ClassCastException e) {
assertTrue(e.getMessage(), e.getMessage().startsWith("Cannot convert '43'"));
}
builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.startsWith("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
list = context.asValue(new String[]{"42", "422"}).as(TARGET_CLASS_LIST);
assertEquals(2, list.size());
assertEquals("42", list.get(0).o);
assertEquals("422", list.get(1).o);
}
static final TypeLiteral<Map<String, TargetClass1>> TARGET_CLASS_MAP_STRING = new TypeLiteral<Map<String, TargetClass1>>() {
};
static final TypeLiteral<Map<Long, TargetClass1>> TARGET_CLASS_MAP_LONG = new TypeLiteral<Map<Long, TargetClass1>>() {
};
@Test
public void testConverterMapLong() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
Map<Long, TargetClass1> map = context.asValue(new String[]{"42", "42"}).as(TARGET_CLASS_MAP_LONG);
assertEquals(2, map.size());
assertEquals("42", map.get(0L).o);
assertEquals("42", map.get(1L).o);
map = context.asValue(new String[]{"42", "43"}).as(TARGET_CLASS_MAP_LONG);
try {
map.get(1L);
fail();
} catch (ClassCastException e) {
assertTrue(e.getMessage(), e.getMessage().startsWith("Cannot convert '43'"));
}
builder = HostAccess.newBuilder();
builder.allowArrayAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.startsWith("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
map = context.asValue(new String[]{"42", "422"}).as(TARGET_CLASS_MAP_LONG);
assertEquals(2, map.size());
assertEquals("42", map.get(0L).o);
assertEquals("422", map.get(1L).o);
}
public static class ConverterMapTestObject {
@Export public String f0;
@Export public String f1;
ConverterMapTestObject(String f0, String f1) {
this.f0 = f0;
this.f1 = f1;
}
}
@Test
public void testConverterMapString() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v != null && v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(TargetClass1.class, String.class, (v) -> v != null && v.o.equals("42"), (v) -> (String) v.o);
setupEnv(builder);
ConverterMapTestObject testObj = new ConverterMapTestObject("42", "42");
Map<String, TargetClass1> map = context.asValue(testObj).as(TARGET_CLASS_MAP_STRING);
assertEquals(2, map.size());
assertEquals("42", map.get("f0").o);
assertEquals("42", map.get("f1").o);
try {
map.put("f0", new TargetClass1("43"));
fail();
} catch (ClassCastException e) {
}
testObj.f0 = null;
map.put("f0", new TargetClass1("42"));
assertEquals("42", testObj.f0);
map = context.asValue(new ConverterMapTestObject("42", "43")).as(TARGET_CLASS_MAP_STRING);
assertEquals("42", map.get("f0").o);
try {
map.get("f1");
fail();
} catch (ClassCastException e) {
assertTrue(e.getMessage(), e.getMessage().startsWith("Cannot convert '43'"));
}
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.startsWith("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
map = context.asValue(new ConverterMapTestObject("42", "422")).as(TARGET_CLASS_MAP_STRING);
assertEquals(2, map.size());
assertEquals("42", map.get("f0").o);
assertEquals("422", map.get("f1").o);
}
@Implementable
public interface ConverterProxy {
TargetClass1 f0();
TargetClass1 f1();
}
@Test
public void testConverterProxy() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v != null && v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(TargetClass1.class, String.class, (v) -> v != null && v.o.equals("42"), (v) -> (String) v.o);
setupEnv(builder);
ConverterMapTestObject testObj = new ConverterMapTestObject("42", "42");
ConverterProxy map = context.asValue(testObj).as(ConverterProxy.class);
assertEquals("42", map.f0().o);
assertEquals("42", map.f1().o);
assertEquals("42", testObj.f0);
map = context.asValue(new ConverterMapTestObject("42", "43")).as(ConverterProxy.class);
assertEquals("42", map.f0().o);
try {
map.f1();
fail();
} catch (ClassCastException e) {
assertTrue(e.getMessage(), e.getMessage().startsWith("Cannot convert '43'"));
}
builder = HostAccess.newBuilder();
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v.startsWith("42"), (v) -> new TargetClass1(v));
setupEnv(builder);
map = context.asValue(new ConverterMapTestObject("42", "422")).as(ConverterProxy.class);
assertEquals("42", map.f0().o);
assertEquals("422", map.f1().o);
}
@FunctionalInterface
public interface ConverterFunction {
TargetClass1 m(String arg0);
}
@Test
public void testConverterFunctional() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowPublicAccess(true);
builder.targetTypeMapping(String.class, TargetClass1.class, (v) -> v != null && v.equals("42"), (v) -> new TargetClass1(v));
builder.targetTypeMapping(TargetClass1.class, String.class, (v) -> v != null && v.o.equals("42"), (v) -> (String) v.o);
setupEnv(builder);
Value identity = context.asValue(new ProxyExecutable() {
public Object execute(Value... arguments) {
return arguments[0];
}
});
ConverterFunction f = identity.as(ConverterFunction.class);
assertEquals("42", f.m("42").o);
}
static class CountingPredicate implements Predicate<Value> {
final String targetString;
int count = 0;
CountingPredicate(String targetString) {
this.targetString = targetString;
}
@Override
public boolean test(Value t) {
count++;
return t.isString() && t.asString().equals(targetString);
}
}
static class CountingConverter implements Function<Value, String> {
int count = 0;
public String apply(Value t) {
count++;
return t.asString();
}
}
@Test
public void testMappingInvocations() {
HostAccess.Builder builder = HostAccess.newBuilder();
CountingPredicate accepts0 = new CountingPredicate("42");
CountingConverter converter0 = new CountingConverter();
builder.targetTypeMapping(Value.class, String.class, accepts0, converter0);
CountingPredicate accepts1 = new CountingPredicate("43");
CountingConverter converter1 = new CountingConverter();
builder.targetTypeMapping(Value.class, String.class, accepts1, converter1);
setupEnv(builder);
context.asValue("42").as(String.class);
assertEquals(1, accepts0.count);
assertEquals(1, converter0.count);
assertEquals(0, accepts1.count);
assertEquals(0, converter1.count);
context.asValue("43").as(String.class);
assertEquals(2, accepts0.count);
assertEquals(1, converter0.count);
assertEquals(1, accepts1.count);
assertEquals(1, converter1.count);
context.asValue("44").as(String.class);
assertEquals(3, accepts0.count);
assertEquals(1, converter0.count);
assertEquals(2, accepts1.count);
assertEquals(1, converter1.count);
}
@Test
public void testAcceptsError() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowPublicAccess(true);
final IllegalArgumentException error = new IllegalArgumentException();
Predicate<Integer> errorAccepts = new Predicate<Integer>() {
public boolean test(Integer t) {
error.initCause(new RuntimeException());
throw error;
}
};
builder.targetTypeMapping(Integer.class, String.class, errorAccepts, (v) -> {
throw new AssertionError();
});
setupEnv(builder);
Value v = context.asValue(42);
try {
v.as(String.class);
fail();
} catch (PolyglotException e) {
assertTrue(e.isHostException());
assertSame(error, e.asHostException());
}
}
@Test
public void testConverterError() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowPublicAccess(true);
final IllegalArgumentException error = new IllegalArgumentException();
builder.targetTypeMapping(Integer.class, String.class, (v) -> v.equals(42), (v) -> {
throw error;
});
setupEnv(builder);
Value v = context.asValue(42);
try {
v.as(String.class);
fail();
} catch (PolyglotException e) {
assertTrue(e.isHostException());
assertSame(error, e.asHostException());
}
}
@Test
public void testConverterClassCast() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.allowPublicAccess(true);
ClassCastException cce = new ClassCastException("foo");
builder.targetTypeMapping(Integer.class, String.class, (v) -> v.equals(42), (v) -> {
throw cce;
});
setupEnv(builder);
Value v = context.asValue(42);
try {
v.as(String.class);
fail();
} catch (ClassCastException e) {
assertEquals("foo", e.getMessage());
assertNotSame(e, cce);
}
}
public static class PassPrimitive {
@Export
public int f0(int arg0) {
return arg0;
}
}
@Test
public void testConverterReturnsNull() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.targetTypeMapping(Integer.class, Integer.class, (v) -> v.equals(42), (v) -> {
return null;
});
setupEnv(builder);
try {
context.asValue(new PassPrimitive()).invokeMember("f0", 42);
fail();
} catch (PolyglotException e) {
assertTrue(e.isHostException());
assertTrue(e.asHostException() instanceof NullPointerException);
}
assertNull(context.asValue(42).as(int.class));
assertEquals(43, context.asValue(43).asInt());
}
@Test
public void testTargetOrderStrict() {
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Integer.class, null,
(v) -> 42, TargetMappingPrecedence.HIGHEST));
assertEquals(42, (int) context.asValue(41).as(int.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Integer.class, null,
(v) -> 42, TargetMappingPrecedence.HIGH));
assertEquals(42, (int) context.asValue(41).as(int.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Integer.class, null,
(v) -> 41, TargetMappingPrecedence.LOW));
assertEquals(42, (int) context.asValue(42).as(int.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Integer.class, null,
(v) -> 41, TargetMappingPrecedence.LOWEST));
assertEquals(42, (int) context.asValue(42).as(int.class));
}
@Test
public void testTargetOrderLoose() {
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Character.class, null,
(v) -> (char) 42, TargetMappingPrecedence.HIGHEST));
assertEquals((char) 42, (char) context.asValue(41).as(char.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Character.class, null,
(v) -> (char) 42, TargetMappingPrecedence.HIGH));
assertEquals((char) 42, (char) context.asValue(41).as(char.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Character.class, null,
(v) -> (char) 42, TargetMappingPrecedence.LOW));
assertEquals((char) 42, (char) context.asValue(41).as(char.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, Character.class, null,
(v) -> (char) 41, TargetMappingPrecedence.LOWEST));
assertEquals((char) 42, (char) context.asValue(42).as(char.class));
}
static class TestProxyExecutable implements ProxyExecutable {
public Object execute(Value... arguments) {
return "OriginalFunction";
}
}
@SuppressWarnings("unchecked")
@Test
public void testTargetOrderFunctionProxy() {
Function<Void, String> convertedFunction = (a) -> "ConvertedFunction";
setupEnv(HostAccess.ALL);
assertEquals("OriginalFunction", ((Function<Void, String>) context.asValue(new TestProxyExecutable()).as(Function.class)).apply(null));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, Function.class, null,
(v) -> convertedFunction, TargetMappingPrecedence.HIGHEST));
assertEquals("ConvertedFunction", ((Function<Void, String>) context.asValue(new TestProxyExecutable()).as(Function.class)).apply(null));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, Function.class, null,
(v) -> convertedFunction, TargetMappingPrecedence.HIGH));
assertEquals("ConvertedFunction", ((Function<Void, String>) context.asValue(new TestProxyExecutable()).as(Function.class)).apply(null));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, Function.class, null,
(v) -> convertedFunction, TargetMappingPrecedence.LOW));
assertEquals("ConvertedFunction", ((Function<Void, String>) context.asValue(new TestProxyExecutable()).as(Function.class)).apply(null));
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, Function.class, null,
(v) -> convertedFunction, TargetMappingPrecedence.LOWEST));
assertEquals("OriginalFunction", ((Function<Void, String>) context.asValue(new TestProxyExecutable()).as(Function.class)).apply(null));
}
public static class NoCoercion {
}
@Implementable
public interface TestInterface {
String test();
}
@SuppressWarnings("unchecked")
@Test
public void testTargetOrderObjectProxy() {
Map<String, Object> originalValues = new HashMap<>();
originalValues.put("test", "OriginalObject");
ProxyObject original = ProxyObject.fromMap(originalValues);
TestInterface converted = new TestInterface() {
public String test() {
return "ConvertedObject";
}
};
setupEnv(HostAccess.ALL);
assertEquals("OriginalObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.HIGHEST));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.HIGH));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.LOW));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.LOWEST));
assertEquals("OriginalObject", context.asValue(original).as(TestInterface.class).test());
}
public static class HostProxyObject {
@Export
public String test() {
return "OriginalObject";
}
}
@Test
public void testTargetOrderHostProxy() {
HostProxyObject original = new HostProxyObject();
TestInterface converted = new TestInterface() {
public String test() {
return "ConvertedObject";
}
};
setupEnv(HostAccess.ALL);
assertEquals("OriginalObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.HIGHEST));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.HIGH));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.LOW));
assertEquals("ConvertedObject", context.asValue(original).as(TestInterface.class).test());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Value.class, TestInterface.class, null,
(v) -> converted, TargetMappingPrecedence.LOWEST));
assertEquals("OriginalObject", context.asValue(original).as(TestInterface.class).test());
}
@SuppressWarnings("unchecked")
@Test
public void testTargetOrderNoCoercion() {
NoCoercion noCoercion = new NoCoercion();
setupEnv(HostAccess.ALL);
assertFails(() -> context.asValue("").as(NoCoercion.class), ClassCastException.class);
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, NoCoercion.class, null,
(v) -> noCoercion, TargetMappingPrecedence.HIGHEST));
assertEquals(noCoercion, context.asValue("").as(NoCoercion.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, NoCoercion.class, null,
(v) -> noCoercion, TargetMappingPrecedence.HIGH));
assertEquals(noCoercion, context.asValue("").as(NoCoercion.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, NoCoercion.class, null,
(v) -> noCoercion, TargetMappingPrecedence.LOW));
assertEquals(noCoercion, context.asValue("").as(NoCoercion.class));
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, NoCoercion.class, null,
(v) -> noCoercion, TargetMappingPrecedence.LOWEST));
assertEquals(noCoercion, context.asValue("").as(NoCoercion.class));
}
@SuppressWarnings("unused")
public static class LooseOverload1 {
@Export
public String m(int s) {
return "int";
}
@Export
public String m(List<Object> s) {
return "list";
}
}
@Test
public void testLooseOverloadAssertion1() {
setupEnv(HostAccess.ALL);
assertFails(() -> context.asValue(new LooseOverload1()).invokeMember("m", (char) 42), IllegalArgumentException.class);
}
@SuppressWarnings("unused")
public static class LooseOverload2 {
@Export
public String m(char s) {
return "int";
}
@Export
public String m(List<Object> s) {
return "list";
}
}
@Test
public void testOverloadAssertion2() {
setupEnv(HostAccess.ALL);
assertEquals("int", context.asValue(new LooseOverload2()).invokeMember("m", 42).asString());
}
@SuppressWarnings("unused")
public static class OverloadPrecedenceStrict {
@Export
public String m(String s) {
return "String";
}
@Export
public String m(int s) {
return "int";
}
}
@Test
public void testConverterOverloadPrecedenceStrict() {
OverloadPrecedenceStrict obj = new OverloadPrecedenceStrict();
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, Integer.class, null,
(v) -> Integer.parseInt(v), TargetMappingPrecedence.HIGHEST));
assertEquals("int", context.asValue(obj).invokeMember("m", "42").asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, Integer.class, null,
(v) -> Integer.parseInt(v), TargetMappingPrecedence.HIGH));
assertFails(() -> context.asValue(obj).invokeMember("m", "42"), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, Integer.class, null,
(v) -> Integer.parseInt(v), TargetMappingPrecedence.LOW));
assertEquals("String", context.asValue(obj).invokeMember("m", "42").asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(String.class, Integer.class, null,
(v) -> Integer.parseInt(v), TargetMappingPrecedence.LOWEST));
assertEquals("String", context.asValue(obj).invokeMember("m", "42").asString());
}
@SuppressWarnings("unused")
public static class OverloadPrecedenceLoose {
@Export
public String m(char s) {
return "int";
}
@Export
public String m(List<Object> s) {
return "list";
}
}
@Test
public void testConverterOverloadPrecedenceLoose() {
OverloadPrecedenceLoose obj = new OverloadPrecedenceLoose();
setupEnv(HostAccess.ALL);
assertEquals("int", context.asValue(obj).invokeMember("m", (char) 42).asString());
assertEquals("int", context.asValue(obj).invokeMember("m", 42).asString());
assertEquals("list", context.asValue(obj).invokeMember("m", ProxyArray.fromArray()).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, List.class, null,
(v) -> Arrays.asList(v), TargetMappingPrecedence.HIGHEST));
assertEquals("list", context.asValue(obj).invokeMember("m", 42).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, List.class, null,
(v) -> Arrays.asList(v), TargetMappingPrecedence.HIGH));
assertEquals("list", context.asValue(obj).invokeMember("m", 42).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, List.class, null,
(v) -> Arrays.asList(v), TargetMappingPrecedence.LOW));
assertEquals("list", context.asValue(obj).invokeMember("m", 42).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Integer.class, List.class, null,
(v) -> Arrays.asList(v), TargetMappingPrecedence.LOWEST));
assertEquals("int", context.asValue(obj).invokeMember("m", 42).asString());
}
@SuppressWarnings("unused")
public static class OverloadPrecedenceFunctionProxy {
@Export
public String m(Function<Value, Value> s) {
return "function";
}
@Export
public String m(Runnable s) {
return "runnable";
}
}
@Test
public void testConverterOverloadPrecedenceFunctionProxy() {
OverloadPrecedenceFunctionProxy obj = new OverloadPrecedenceFunctionProxy();
ProxyExecutable f = new ProxyExecutable() {
@Override
public Object execute(Value... arguments) {
return 42;
}
};
setupEnv(HostAccess.ALL);
assertEquals("function", context.asValue(obj).invokeMember("m", f).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Function.class, Runnable.class, null,
(v) -> null, TargetMappingPrecedence.HIGHEST));
assertEquals("runnable", context.asValue(obj).invokeMember("m", f).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Function.class, Runnable.class, null,
(v) -> null, TargetMappingPrecedence.HIGH));
assertEquals("runnable", context.asValue(obj).invokeMember("m", f).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Function.class, Runnable.class, null,
(v) -> null, TargetMappingPrecedence.LOW));
assertFails(() -> context.asValue(obj).invokeMember("m", f), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().targetTypeMapping(Function.class, Runnable.class, null,
(v) -> null, TargetMappingPrecedence.LOWEST));
assertEquals("function", context.asValue(obj).invokeMember("m", f).asString());
}
@Implementable
public interface TestObjectProxy1 {
String test();
}
@Implementable
public interface TestObjectProxy2 {
String test();
}
@SuppressWarnings("unused")
public static class OverloadPrecedenceObjectProxy {
@Export
public String m(TestObjectProxy1 s) {
return "proxy1";
}
@Export
public String m(TestObjectProxy2 s) {
return "proxy2";
}
}
@Test
public void testConverterOverloadPrecedenceObjectProxy() {
Map<String, Object> originalValues = new HashMap<>();
originalValues.put("test", "OriginalObject");
ProxyObject arg = ProxyObject.fromMap(originalValues);
OverloadPrecedenceObjectProxy obj = new OverloadPrecedenceObjectProxy();
setupEnv(HostAccess.ALL);
assertFails(() -> context.asValue(obj).invokeMember("m", arg), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().targetTypeMapping(Map.class, TestObjectProxy2.class, null,
(v) -> null, TargetMappingPrecedence.HIGHEST));
assertEquals("proxy2", context.asValue(obj).invokeMember("m", arg).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Map.class, TestObjectProxy2.class, null,
(v) -> null, TargetMappingPrecedence.HIGH));
assertEquals("proxy2", context.asValue(obj).invokeMember("m", arg).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Map.class, TestObjectProxy2.class, null,
(v) -> null, TargetMappingPrecedence.LOW));
assertEquals("proxy2", context.asValue(obj).invokeMember("m", arg).asString());
setupEnv(HostAccess.newBuilder().targetTypeMapping(Map.class, TestObjectProxy2.class, null,
(v) -> null, TargetMappingPrecedence.LOWEST));
assertFails(() -> context.asValue(obj).invokeMember("m", arg), IllegalArgumentException.class);
}
@SuppressWarnings("unused")
public static class OverloadPrecedenceAmbiguous {
@Export
public String m(String s) {
return "string";
}
@Export
public String m(int s) {
return "int";
}
}
@Test
public void testConverterOverloadPrecedenceExample() {
OverloadPrecedenceAmbiguous obj = new OverloadPrecedenceAmbiguous();
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.HIGHEST));
assertEquals("int", context.asValue(obj).invokeMember("m", "").asString());
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.HIGH));
assertFails(() -> context.asValue(obj).invokeMember("m", ""), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.LOW));
assertEquals("string", context.asValue(obj).invokeMember("m", "").asString());
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.LOWEST));
assertEquals("string", context.asValue(obj).invokeMember("m", "").asString());
}
@Test
public void testConverterOverloadPrecedenceAmbiguous2() {
Map<String, Object> originalValues = new HashMap<>();
originalValues.put("test", "OriginalObject");
OverloadPrecedenceAmbiguous obj = new OverloadPrecedenceAmbiguous();
setupEnv(HostAccess.ALL);
assertEquals("string", context.asValue(obj).invokeMember("m", "").asString());
assertEquals("int", context.asValue(obj).invokeMember("m", 42).asString());
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.HIGHEST).
targetTypeMapping(String.class, String.class, null, (v) -> "42", TargetMappingPrecedence.HIGHEST));
assertFails(() -> context.asValue(obj).invokeMember("m", ""), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.HIGH).
targetTypeMapping(String.class, String.class, null, (v) -> "42", TargetMappingPrecedence.HIGH));
assertFails(() -> context.asValue(obj).invokeMember("m", ""), IllegalArgumentException.class);
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.LOW).
targetTypeMapping(String.class, String.class, null, (v) -> "42", TargetMappingPrecedence.LOW));
assertEquals("string", context.asValue(obj).invokeMember("m", "").asString());
assertEquals("int", context.asValue(obj).invokeMember("m", 42).asString());
setupEnv(HostAccess.newBuilder().
targetTypeMapping(String.class, Integer.class, null, (v) -> 42, TargetMappingPrecedence.LOWEST).
targetTypeMapping(String.class, String.class, null, (v) -> "42", TargetMappingPrecedence.LOWEST));
assertEquals("string", context.asValue(obj).invokeMember("m", "").asString());
assertEquals("int", context.asValue(obj).invokeMember("m", 42).asString());
}
@SuppressWarnings("unused")
public static class VarArgsFunctionPrecedence {
@Export
public String m(String name, Function<Integer, Integer> func, Object... args) {
func.apply(42);
return "function";
}
@Export
public String m(String name, Object... args) {
return "object";
}
}
@Test
public void testVarArgsFunctionPrecedence() {
VarArgsFunctionPrecedence obj = new VarArgsFunctionPrecedence();
ProxyExecutable f = arguments -> 42;
setupEnv(HostAccess.ALL);
assertEquals("function", context.asValue(obj).invokeMember("m", "dummy", f).asString());
assertEquals("function", context.asValue(obj).invokeMember("m", "dummy", f, 1, 2, 3).asString());
assertEquals("object", context.asValue(obj).invokeMember("m", "dummy").asString());
assertEquals("object", context.asValue(obj).invokeMember("m", "dummy", "dummy").asString());
}
@SuppressWarnings("unused")
public static class ListVsArray {
@Export
public String m(List<Object> list) {
return "list";
}
@Export
public String m(Object[] array) {
return "array";
}
}
@Test
public void testListVsArray() {
ListVsArray obj = new ListVsArray();
ProxyArray a = ProxyArray.fromArray(4, 5, 6);
setupEnv(HostAccess.ALL);
assertEquals("list", context.asValue(obj).invokeMember("m", a).asString());
}
@Test
public void testReturnsNull() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.targetTypeMapping(Integer.class, Integer.class, (v) -> v.equals(42), (v) -> {
return null;
});
setupEnv(builder);
try {
context.asValue(new PassPrimitive()).invokeMember("f0", 42);
fail();
} catch (PolyglotException e) {
assertTrue(e.isHostException());
assertTrue(e.asHostException() instanceof NullPointerException);
}
assertNull(context.asValue(42).as(int.class));
assertEquals(43, context.asValue(43).asInt());
}
@Test
public void testPassNullValue() {
HostAccess.Builder builder = HostAccess.newBuilder();
AtomicInteger invoked = new AtomicInteger();
builder.targetTypeMapping(Value.class, Integer.class, (v) -> {
assertTrue(v.isNull());
invoked.incrementAndGet();
return v.fitsInInt();
}, (v) -> {
throw new AssertionError();
});
setupEnv(builder);
assertNull(context.asValue(null).as(Integer.class));
assertEquals(1, invoked.get());
}
@Test
public void testPassNullObject() {
HostAccess.Builder builder = HostAccess.newBuilder();
AtomicInteger invoked = new AtomicInteger();
builder.targetTypeMapping(Integer.class, Integer.class, (v) -> {
assertNull(v);
invoked.incrementAndGet();
return true;
}, (v) -> {
return null;
});
setupEnv(builder);
assertNull(context.asValue(null).as(Integer.class));
assertEquals(1, invoked.get());
}
@Test
@Ignore
public void testRecursion() {
HostAccess.Builder builder = HostAccess.newBuilder();
builder.targetTypeMapping(Value.class, Integer.class, (v) -> {
return true;
}, (v) -> {
return v.as(Integer.class);
});
setupEnv(builder);
try {
assertNull(context.asValue(42).as(Integer.class));
fail();
} catch (PolyglotException e) {
if (e.isGuestException()) {
assertTrue(e.isInternalError());
} else {
assertTrue(e.isHostException());
assertTrue(e.asHostException() instanceof StackOverflowError);
}
}
}
public static class TestIdentity {
public void foo() {
}
}
public interface TestIdentityMapping {
void foo();
}
@Test
public void testIdentity() {
setupEnv(HostAccess.EXPLICIT);
Context c1 = this.context;
Context c2 = Context.create();
TestIdentity v0 = new TestIdentity();
TestIdentity v1 = new TestIdentity();
assertFalse(c1.asValue(v0).equals(c1.asValue(v1)));
assertFalse(c1.asValue(v0).equals(c2.asValue(v1)));
assertTrue(c1.asValue(v0).equals(c1.asValue(v0)));
assertTrue(c1.asValue(v1).equals(c2.asValue(v1)));
c2.close();
}
public static class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls) {
super(urls);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
}
}
}