GraalVM native-image dynamic proxy fails to invoke methods defined in super-interfaces

89 Views Asked by At

I'm trying to get native-image to work in this situation: I have a dynamic proxy on an interface, which has a super-interface that declares a default method. Invoking this default method from the sub-interface always fails.

I tried to put various entries in proxy-config.json and reflect-config.json, including what was generated by the native-image agent, but I just cannot get it to work. I tried with GraalVM 21 and 17. Any ideas what I'm doing wrong? Here's the code. I know it is a bit of a contrived example. This is the minimal version I could extract from a much more complicated code-base.

package nativetest;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        Class<?> c = TestSubInterface.class;
        TestInvocationHandler<?> handler = new TestInvocationHandler<>(c);
        TestSubInterface proxy = (TestSubInterface) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class<?>[] { c }, handler);

        System.out.println("Result: " + proxy.doSomething());
    }

    public static class TestInvocationHandler<T> implements InvocationHandler {
        private final Class<T> configClass;

        public TestInvocationHandler(Class<T> configClass) {
            this.configClass = configClass;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MethodHandle methodHandle = MethodHandles.lookup()
                    .findSpecial(configClass, method.getName(),
                            MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
                            configClass);
            return methodHandle.bindTo(proxy).invoke();
        }
    }

    public interface TestInterface {
        default String doSomething() {
            System.out.println("Doing something");
            return "Something done";
        }
    }

    public interface TestSubInterface extends TestInterface {

    }
}

The exception I'm getting is

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
    at jdk.proxy4/jdk.proxy4.$Proxy48.doSomething(Unknown Source)
    at nativetest.Main.main(Main.java:17)
    at [email protected]/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.NoSuchMethodException: no such method: nativetest.Main$TestSubInterface.doSomething()String/invokeSpecial
    at [email protected]/java.lang.invoke.MemberName.makeAccessException(MemberName.java:915)
    at [email protected]/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:994)
    at [email protected]/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3750)
    at [email protected]/java.lang.invoke.MethodHandles$Lookup.findSpecial(MethodHandles.java:3097)
    at nativetest.Main$TestInvocationHandler.invoke(Main.java:30)
    ... 3 more
Caused by: java.lang.NoSuchMethodError: nativetest.Main$TestSubInterface.doSomething()
    at org.graalvm.nativeimage.builder/com.oracle.svm.core.methodhandles.Util_java_lang_invoke_MethodHandleNatives.resolve(Target_java_lang_invoke_MethodHandleNatives.java:335)
    at [email protected]/java.lang.invoke.MethodHandleNatives.resolve(MethodHandleNatives.java:213)
    at [email protected]/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:962)
    at [email protected]/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:991)
    ... 6 more

When running with java -jar, it works just fine btw.

0

There are 0 best solutions below