oracle/graal

`jdkProxyObj.getClass().getMethod("xxx")` will throw `java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx()` in the `native-image`.

wangliang181230 opened this issue · 13 comments

Jdk proxy object can't get method by the Method Class.getMethod(methodName) in the native-image.

jdkProxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image.

Example project

https://github.com/wangliang181230/example__oracle_graal_issue-6079

Some code

TestInterface.java

public interface TestInterface {

    void foo();

    void bar();

}

ProxyReflectionExample.java

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyReflectionExample {

    public static void main(String[] args) throws Exception {
        TestInterface proxyObj = (TestInterface)Proxy.newProxyInstance(
            ProxyReflectionExample.class.getClassLoader(),
            new Class[] { TestInterface.class },
            (proxy, method, args1) -> {
                System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
                return null;
            });

        try {
            // It will throw `NoSuchMethodException` in `native-image`.
            Method method = proxyObj.getClass().getMethod("foo");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(proxyObj);

            method = proxyObj.getClass().getMethod("bar");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(proxyObj);
        } catch (Throwable t) {
            t.printStackTrace();
        }

        Thread.sleep(10000);
    }
}

reflect-config.json

[
    {
        "name": "TestInterface",
        "methods": [
            { "name": "foo", "parameterTypes": [] },
            { "name": "bar", "parameterTypes": [] }
        ]
    }
]

Console output in JVM

Method: foo,     Declaring Class: jdk.proxy1.$Proxy0
Method: foo,     Declaring Class: TestInterface
Method: bar,     Declaring Class: jdk.proxy1.$Proxy0
Method: bar,     Declaring Class: TestInterface

Error log in native-image

java.lang.NoSuchMethodException: jdk.proxy4.$Proxy60.foo()
        at java.base@17.0.6/java.lang.Class.getMethod(DynamicHub.java:2227)
        at ProxyReflectionExample.main(ProxyReflectionExample.java:17)

Environment and versions:

  1. OS: Windows 10
  2. GraalVM: graalvm-ce-java17-22.3.1 Windows (amd64)

How can I solve this problem?

Do you expect the Proxy to be registered automatically? If not, you would need to register it manually. Please see https://www.graalvm.org/22.0/reference-manual/native-image/DynamicProxy/ -

Also, please add the GraalVM version, which you used.

Do you expect the Proxy to be registered automatically? If not, you would need to register it manually. Please see https://www.graalvm.org/22.0/reference-manual/native-image/DynamicProxy/ -

My question is not about dynamic proxy, but about the acquisition of dynamic proxy methods.
For example proxyObj.getClass().getMethod(methodName) will throw NoSuchMethodException in native-image.
Of course, I have added the metadata of the interfaces to reflect-config.json and proxy-config.json and successfully created the proxy object in native-image, but the class of the proxy object is dynamic and cannot be added the reflect metadata.

Also, please add the GraalVM version, which you used.

graalvm-ce-java17-22.3.1 Windows (amd64)

This will cause the semantic inconsistency of proxyObject.getClass().getMethod(methodName) in JVM and native-image.

Could you maybe add the stacktrace and the config files to this issue?

Could you maybe add the stacktrace and the config files to this issue?

I modified the content of this issue. PTAL

Thank you for sharing this, we'll take a look into it shortly

This is currently not supported in the metadata. We should allow for proxies to be marked with an interface list:

  {
        "proxy": ["TestInterface"],
        "methods": [
            { "name": "foo", "parameterTypes": [] },
            { "name": "bar", "parameterTypes": [] }
        ]
    }

Until we introduce that feature, a possible workaround is to use initialization at build time. Rewrite the program in the following way:

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface TestInterface {

    void foo();

    void bar();

}

public class ProxyReflectionExample {

    static class ProxyReflectionStatics {
        static final TestInterface proxyObj = (TestInterface) Proxy.newProxyInstance(
                        ProxyReflectionExample.class.getClassLoader(),
                        new Class[]{TestInterface.class},
                        (proxy, method, args1) -> {
                            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
                            return null;
                        });

    }

    public static void main(String[] args) throws Exception {
        try {
            // It will throw `NoSuchMethodException` in `native-image`.
            Method method = ProxyReflectionStatics.proxyObj.getClass().getMethod("foo");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(ProxyReflectionStatics.proxyObj);

            method = ProxyReflectionStatics.proxyObj.getClass().getMethod("bar");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(ProxyReflectionStatics.proxyObj);
        } catch (Throwable t) {
            t.printStackTrace();
        }

        Thread.sleep(10000);
    }
}

and build it with:

native-image ProxyReflectionExample --initialize-at-build-time=ProxyReflectionExample\$ProxyReflectionStatics

@vjovanov
However, in many applications, it is not known which interface to proxy.
The above code is only an example I provided.