Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Default Methods on interfaces #813

Closed
Master-01 opened this issue May 20, 2017 · 9 comments
Closed

Support Default Methods on interfaces #813

Master-01 opened this issue May 20, 2017 · 9 comments

Comments

@Master-01
Copy link

Master-01 commented May 20, 2017

Hellow!

I use Java8 interface
public static interface IKernelApiAll extends Kernel32
{
@Override public HANDLE GetCurrentThread();
public int SetThreadAffinityMask(final Pointer hThread, final int dwThreadAffinityMask);
default public int SetCurrentThreadAffinityMask(final int dwThreadAffinityMask) { return SetThreadAffinityMask(GetCurrentThread().getPointer(), dwThreadAffinityMask); }
}
when call SetCurrentThreadAffinityMask(0) I get an error
Exception in thread "thread1" java.lang.UnsatisfiedLinkError: Error looking up function 'SetCurrentThreadAffinityMask': not found procedure
at com.sun.jna.Function.(Function.java:245)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:566)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:542)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:528)
at com.sun.jna.Library$Handler.invoke(Library.java:228)
at com.sun.proxy.$Proxy8.SetCurrentThreadAffinityMask(Unknown Source)

Why JNA is trying to find a procedure in the DLL if the keyword is specified in the declaration as default.
The default functions should simply be ignored for find prodedure procedures in the DLL, and simple execute default java code!

@matthiasblaesing
Copy link
Member

JNA aims for a compatibility with older Java versions. Default interfaces were added Java 8 and the instrumentation for default methods was also added in Java 8.

This is what happens:

  • all function calls for the interface methods are passed to the Proxy Object
  • the proxy object does not "know" that the call is a default method
  • the call is interpretated in the normal manor

So what would need to be implemented:

  • detection of a call for a default method (requires Java 8)
  • Method for invocation of the default method from the Proxy (requires Java 8)

With reflection this could be implemented in a backwards compatible manner. At this point default methods can't be used with JNA.

Please note: Issues should be discussed on the jna-users user forum first (as noted in the issue template).

@matthiasblaesing matthiasblaesing changed the title Why not ignored java8 interface function with default keyword ? Support Default Methods on interfaces May 20, 2017
@matthiasblaesing
Copy link
Member

It might be necessary to cover different invocation handlers at least com.sun.jna.Library.Handler and com.sun.jna.platform.win32.COM.util.ProxyObject need to be covered.

@matthiasblaesing
Copy link
Member

@joerg1985 @Master-01 if one of you wants to have a try. I did some experimenting here:
https://github.com/matthiasblaesing/jna/tree/default_methods
This fails in JDK 11 and I'm not sure where to go on on the java level. Maybe it is worth using JNI to call default methods.

@matthiasblaesing
Copy link
Member

@joerg1985 @Master-01 I updated the branch so that actually works. I tested it on Java 8 + 11. Testing/comments would be appreciated!

@joerg1985
Copy link
Contributor

joerg1985 commented Mar 11, 2019

@matthiasblaesing thanks for the fast response, i tryed it but it throws an IllegalArgumentException (see stacktrace below). I think the call to ReflectionUtils.invokeDefaultMethod is not correct, the proxy object is passed twice and the method is ignored.

But even if i change the method call to: return ReflectionUtils.invokeDefaultMethod(proxy, method, args); the exceptions stayed the same.

Update: I think the call should be return ReflectionUtils.invokeDefaultMethod(rawDispatch, method, args);. I will try this as soon as possible and give you another update on this.

Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.sun.jna.internal.ReflectionUtils.invokeDefaultMethod(ReflectionUtils.java:202)
at com.sun.jna.platform.win32.COM.util.ProxyObject.invoke(ProxyObject.java:221)
at com.sun.proxy.$Proxy11.Add(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.sun.jna.platform.win32.COM.util.Factory$ProxyObject2$1.call(Factory.java:98)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:205)
at com.sun.jna.platform.win32.COM.util.ComThread.execute(ComThread.java:157)
at com.sun.jna.platform.win32.COM.util.Factory.runInComThread(Factory.java:172)
at com.sun.jna.platform.win32.COM.util.Factory.access$100(Factory.java:56)
at com.sun.jna.platform.win32.COM.util.Factory$ProxyObject2.invoke(Factory.java:95)
at com.sun.proxy.$Proxy11.Add(Unknown Source)
...

@matthiasblaesing
Copy link
Member

@joerg1985 I screwed the implementation in ProxyObject up (I should not commit right before I want to go to bed). This should work (ProxyObject, line 220):

        if((!declaredAsInterface) && ReflectionUtils.isDefault(method)) {
            Object methodHandle = ReflectionUtils.getMethodHandle(method);
            return ReflectionUtils.invokeDefaultMethod(proxy, methodHandle, args);
        }

I'm currently rebuilding my windows test setup and run a test on it also.

@matthiasblaesing
Copy link
Member

@joerg1985 I pushed updates to the referenced branch. I ran the unittest on windows and it worked. Please see if it works for you.

@joerg1985
Copy link
Contributor

@matthiasblaesing 👍 this time it works as expected, thanks a lot.

@matthiasblaesing
Copy link
Member

Thank you for testing. Merged the changeset to master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants