6953246: JSR 292 should support SAM conversion
Summary: Conversion function MethodHandles.asInstance; initial slow implementation based on Proxy.
Reviewed-by: twisti
diff --git a/test/java/dyn/MethodHandlesTest.java b/test/java/dyn/MethodHandlesTest.java
index 734b132..62b0c90 100644
--- a/test/java/dyn/MethodHandlesTest.java
+++ b/test/java/dyn/MethodHandlesTest.java
@@ -265,6 +265,12 @@
// wrap = Wrapper.forWrapperType(dst);
// if (wrap != Wrapper.OBJECT)
// return wrap.wrap(nextArg++);
+ if (param.isInterface()) {
+ for (Class<?> c : param.getClasses()) {
+ if (param.isAssignableFrom(c) && !c.isInterface())
+ { param = c; break; }
+ }
+ }
if (param.isInterface() || param.isAssignableFrom(String.class))
return "#"+nextArg();
else
@@ -380,7 +386,7 @@
}
public static interface IntExample {
public void v0();
- static class Impl implements IntExample {
+ public static class Impl implements IntExample {
public void v0() { called("Int/v0", this); }
final String name;
public Impl() { name = "Impl#"+nextArg(); }
@@ -1956,6 +1962,107 @@
mh.invokeVarargs(args);
assertCalled(name, args);
}
+
+ static void runForRunnable() {
+ called("runForRunnable");
+ }
+ private interface Fooable {
+ Object foo(Fooable x, Object y);
+ // this is for randomArg:
+ public class Impl implements Fooable {
+ public Object foo(Fooable x, Object y) {
+ throw new RuntimeException("do not call");
+ }
+ final String name;
+ public Impl() { name = "Fooable#"+nextArg(); }
+ @Override public String toString() { return name; }
+ }
+ }
+ static Object fooForFooable(Fooable x, Object y) {
+ return called("fooForFooable", x, y);
+ }
+ private static class MyCheckedException extends Exception {
+ }
+ private interface WillThrow {
+ void willThrow() throws MyCheckedException;
+ }
+
+ @Test
+ public void testAsInstance() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ Lookup lookup = MethodHandles.lookup();
+ {
+ MethodType mt = MethodType.methodType(void.class);
+ MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
+ Runnable proxy = MethodHandles.asInstance(mh, Runnable.class);
+ proxy.run();
+ assertCalled("runForRunnable");
+ }
+ {
+ MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class);
+ MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt);
+ Fooable proxy = MethodHandles.asInstance(mh, Fooable.class);
+ Object[] args = randomArgs(mt.parameterArray());
+ Object result = proxy.foo((Fooable) args[0], args[1]);
+ assertCalled("fooForFooable", args);
+ assertEquals(result, logEntry("fooForFooable", args));
+ }
+ for (Throwable ex : new Throwable[] { new NullPointerException("ok"),
+ new InternalError("ok"),
+ new Throwable("fail"),
+ new Exception("fail"),
+ new MyCheckedException()
+ }) {
+ MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
+ mh = MethodHandles.insertArguments(mh, 0, ex);
+ WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class);
+ try {
+ proxy.willThrow();
+ System.out.println("Failed to throw: "+ex);
+ assertTrue(false);
+ } catch (Throwable ex1) {
+ if (verbosity > 2) {
+ System.out.println("throw "+ex);
+ System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1));
+ }
+ if (ex instanceof RuntimeException ||
+ ex instanceof Error) {
+ assertSame("must pass unchecked exception out without wrapping", ex, ex1);
+ } else if (ex instanceof MyCheckedException) {
+ assertSame("must pass declared exception out without wrapping", ex, ex1);
+ } else {
+ assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
+ UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
+ assertSame(ex, utex.getCause());
+ }
+ }
+ }
+ // Test error checking:
+ MethodHandle genericMH = ValueConversions.varargsArray(0);
+ genericMH = MethodHandles.convertArguments(genericMH, genericMH.type().generic());
+ for (Class<?> sam : new Class[] { Runnable.class,
+ Fooable.class,
+ Iterable.class }) {
+ try {
+ // Must throw, because none of these guys has generic type.
+ MethodHandles.asInstance(genericMH, sam);
+ System.out.println("Failed to throw");
+ assertTrue(false);
+ } catch (IllegalArgumentException ex) {
+ }
+ }
+ for (Class<?> nonSAM : new Class[] { Object.class,
+ String.class,
+ CharSequence.class,
+ Example.class }) {
+ try {
+ MethodHandles.asInstance(ValueConversions.varargsArray(0), nonSAM);
+ System.out.println("Failed to throw");
+ assertTrue(false);
+ } catch (IllegalArgumentException ex) {
+ }
+ }
+ }
}
// Local abbreviated copy of sun.dyn.util.ValueConversions
class ValueConversions {