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

Writing callback function pointers leads to unexpected object creation #925

Closed
lwahonen opened this issue Feb 23, 2018 · 6 comments
Closed

Comments

@lwahonen
Copy link

lwahonen@35acd04

See above commit for a minimal unit test.

It fails with

java.lang.IllegalArgumentException: Structure field "callback" was declared as interface com.sun.jna.CallbacksTest$vTable$functionpointer, which is not supported within a Structure

at com.sun.jna.Structure.writeField(Structure.java:864)
at com.sun.jna.Structure.write(Structure.java:768)
at com.sun.jna.CallbacksTest.testWriteCallback(CallbacksTest.java:1577)
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:564)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:84)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Caused by: java.lang.IllegalArgumentException: Exception thrown while instantiating an instance of class com.sun.jna.platform.win32.OaIdl$SAFEARRAY
at com.sun.jna.Structure.newInstance(Structure.java:1790)
at com.sun.jna.Structure.validate(Structure.java:2128)
at com.sun.jna.CallbackReference.getNativeType(CallbackReference.java:275)
at com.sun.jna.CallbackReference.(CallbackReference.java:248)
at com.sun.jna.CallbackReference.getFunctionPointer(CallbackReference.java:449)
at com.sun.jna.CallbackReference.getFunctionPointer(CallbackReference.java:426)
at com.sun.jna.Pointer.setValue(Pointer.java:885)
at com.sun.jna.Structure.writeField(Structure.java:856)
... 20 more
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
at com.sun.jna.Structure.newInstance(Structure.java:1771)
... 27 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: 0
at com.sun.jna.Structure.toArray(Structure.java:1551)
at com.sun.jna.Structure.toArray(Structure.java:1576)
at com.sun.jna.platform.win32.OaIdl$SAFEARRAY.read(OaIdl.java:610)
at com.sun.jna.platform.win32.OaIdl$SAFEARRAY.(OaIdl.java:604)
... 32 more

If you change the parameter type for runtimeId to int, the test passes successfully.

Not sure why writing a function pointer ends up trying to create a SAFEARRAY object. I can patch this by getting the SAFEARRAY as Pointer to the callback, and constructing the SAFEARRAY manually in the callback.

@matthiasblaesing
Copy link
Member

This sounds like a bug in the override SAFEARRAY#read. Could you please check if this helps:

# This patch file was generated by NetBeans IDE
# It uses platform neutral UTF-8 encoding and \n newlines.
--- a/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java
@@ -607,8 +607,12 @@
         @Override
         public void read() {
             super.read();
+            if(cDims.intValue() > 0) {
             rgsabound = (SAFEARRAYBOUND[]) rgsabound[0].toArray(cDims.intValue());
+            } else {
+                rgsabound = new SAFEARRAYBOUND[]{ new SAFEARRAYBOUND() };
         }
+        }
         
         @Override
         protected List<String> getFieldOrder() {

This is a degenerated case - it is a SAFEARRAY without dimensions. Structure#validate checks the type by trying to instantiate the structure, and this runs into this issue.

@lwahonen
Copy link
Author

Hi,

I think it's leading towards Structure.java [185]:

protected Structure(Pointer p, int alignType, TypeMapper mapper) {
    setAlignType(alignType);
    setStringEncoding(Native.getStringEncoding(getClass()));
    initializeTypeMapper(mapper);
    validateFields();
    **if (p != null) {**
        useMemory(p, 0, true);
    }
    else {
        allocateMemory(CALCULATE_SIZE);
    }
    initializeFields();
}

As P is not null here, it's native@0x0. Should a zero valued pointer be treated as a null pointer here?

@lwahonen
Copy link
Author

For the record, your fix solves this problem. I'm just sleuthing to see if this is a more systemic problem in JNA

@matthiasblaesing
Copy link
Member

@lwahonen could could you please have a look at PR #926? This holds the necessary changeset.

For your question and reference in Structure.java, you are in prinicipal right, that for the PLACEHOLDER_MEMORY case p is a C null pointer. The code in Structure#read and Structure#write handles this case by preventing writes and reads if the backing memory of the structure is the PLACEHOLDER_MEMORY.

@lwahonen
Copy link
Author

The safearray fix fixes things, but honestly I'm not familiar enough with the inner workings of Structure.newInstance to comment on that part. Looks good to me, and I think the worst thing that can happen is moving an error from validation time to actual call time.

@matthiasblaesing
Copy link
Member

Thanks for your input - I merged the changeset to master and the 4.5.X branch.

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

No branches or pull requests

2 participants