diff options
author | Sven Gothel <[email protected]> | 2023-06-29 03:50:12 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-06-29 03:50:12 +0200 |
commit | 6591f1fef419841660311bbb554aeda7b267c9a7 (patch) | |
tree | aa5dba474e78f83628e1cfa7a700797c3f848594 /src/junit/com/jogamp | |
parent | 1d66739e09899cb90888c8fe34aba339511aa656 (diff) |
GlueGen JavaCallback: 1st Working Draft: Changed 'JavaCallbackDef' config, added code generation incl. native to Java dispatch and resource management
Tested via Test4JavaCallback.java (using test2.[hc]).
Please read the GlueGen_Mapping.md as well as Test4JavaCallback.java .
+++
Some implementation details:
JavaConfiguration maps JavaCallbackDef to JavaCallback set-function and maintains a list.
JavaCallbackDef itself holds all configured details.
JavaConfiguration also maps JavaCallbackInfo to JavaCallback set-function.
JavaCallbackInfo itself holds all compile time information, as produced by JavaEmitter.beginFunctions(..).
This extends JavaCallbackDef and avoid repetetive computation for the callback-function-type and its MethodBinding,
parameter indices for the callback interface and userParam, etc.
CMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallback setter function creates a native 'UserParam' struct instance,
which holds the callback-interface-jobject, its callback-jmethodID and
the userParam-jobject for invocation of the actual JavaCallback interface method.
- To produce the C-Type -> JNI-Type conversion, An internal CMethodBindingEmitter instance
for the native-callback function binding is created inside the CMethodBindingEmitter of the callback setter method.
It is being used to map the types to JNI within the generated native callback function,
passed to the actual JavaCallback method.
JavaMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallbacl setter passes the callback-interface-object, the userParam-object and the
callback-method-signature (to have the native method retrieve the jmethodID).
- It receives the native pointer of the native `UserParam` struct instance,
which gets mapped to the userParam-object. (*TODO: Refine ownership + release*).
Diffstat (limited to 'src/junit/com/jogamp')
5 files changed, 235 insertions, 36 deletions
diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java index ae47b43..5871fc0 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java @@ -1086,7 +1086,7 @@ public class BaseClass extends SingletonJunitCase { } } - void assertAPTR(final long expected, final long actual) { + public static void assertAPTR(final long expected, final long actual) { System.err.println("0x"+Long.toHexString(expected)+" == 0x"+Long.toHexString(actual)); if (Platform.is32Bit()) { int exp32; diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java new file mode 100644 index 0000000..1baad3f --- /dev/null +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java @@ -0,0 +1,143 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.gluegen.test.junit.generation; + +import java.io.IOException; + +import com.jogamp.common.os.NativeLibrary; +import com.jogamp.gluegen.test.junit.generation.Bindingtest2.T2_CallbackFunc01; +import com.jogamp.gluegen.test.junit.generation.impl.Bindingtest2Impl; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test {@link Bindingtest2} with {@link T2_PointerStorage} instance and pointer pointer.. + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class Test4JavaCallback extends BaseClass { + static NativeLibrary dynamicLookupHelper; + + /** + * Verifies loading of the new library. + */ + @BeforeClass + public static void chapter__TestLoadLibrary() throws Exception { + BindingJNILibLoader.loadBindingtest2(); + dynamicLookupHelper = NativeLibrary.open("test2", false, false, Test2FuncPtr.class.getClassLoader(), true); + Assert.assertNotNull("NativeLibrary.open(test2) failed", dynamicLookupHelper); + + Bindingtest2Impl.resetProcAddressTable(dynamicLookupHelper); + } + + /** + * Verifies unloading of the new library. + */ + @AfterClass + public static void chapter0XTestUnloadLibrary() throws Exception { + Assert.assertNotNull(dynamicLookupHelper); + dynamicLookupHelper.close(); + dynamicLookupHelper = null; + } + + /** + * Test Bindingtest2 with T2_CallbackFunc JavaCallback + */ + @Test + public void chapter01() throws Exception { + final Bindingtest2 bt2 = new Bindingtest2Impl(); + + final long[] id_res = { -1 }; + final String[] msg_res = { null }; + final T2_CallbackFunc01 myCallback = new T2_CallbackFunc01() { + @Override + public void callback(final long id, final String msg, final Object userParam) { + final MyUserParam myUserParam = (MyUserParam)userParam; + id_res[0] = id + myUserParam.i; + msg_res[0] = msg; + myUserParam.j += id_res[0]; + System.err.println("chapter10.myCallback: "+id+", '"+msg+"'"); + } + }; + final MyUserParam myUserParam = new MyUserParam(10); + Assert.assertEquals(10, myUserParam.i); + Assert.assertEquals( 0, myUserParam.j); + Assert.assertEquals(false, bt2.isMessageCallback01Mapped(myUserParam)); + + bt2.MessageCallback01(myCallback, myUserParam); + Assert.assertEquals(true, bt2.isMessageCallback01Mapped(myUserParam)); + Assert.assertEquals(-1, id_res[0]); + Assert.assertEquals(null, msg_res[0]); + Assert.assertEquals(10, myUserParam.i); + Assert.assertEquals( 0, myUserParam.j); + + { + final String msgNo1 = "My First JavaCallback message"; + bt2.InjectMessageCallback01(404, msgNo1); + Assert.assertEquals(404+10, id_res[0]); + Assert.assertEquals(msgNo1, msg_res[0]); + Assert.assertEquals( 10, myUserParam.i); + Assert.assertEquals(404+10, myUserParam.j); + } + final String msgNo2 = "My Second JavaCallback message"; + { + bt2.InjectMessageCallback01( 42, msgNo2); + Assert.assertEquals( 42+10, id_res[0]); + Assert.assertEquals( msgNo2, msg_res[0]); + Assert.assertEquals( 10, myUserParam.i); + Assert.assertEquals(42+10+404+10, myUserParam.j); + } + bt2.MessageCallback01(null, myUserParam); + Assert.assertEquals(false, bt2.isMessageCallback01Mapped(myUserParam)); + { + final String msgNo3 = "My Third JavaCallback message"; + bt2.InjectMessageCallback01( 21, msgNo3); + // No callback shall be received, hence old values + Assert.assertEquals( 42+10, id_res[0]); + Assert.assertEquals( msgNo2, msg_res[0]); + Assert.assertEquals( 10, myUserParam.i); + Assert.assertEquals(42+10+404+10, myUserParam.j); + } + } + private static class MyUserParam { + final long i; + long j; + public MyUserParam(final long i) { this.i = i; j=0; } + } + + public static void main(final String args[]) throws IOException { + final String tstname = Test4JavaCallback.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } +} diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c index e5c1fe2..9715f11 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c @@ -26,14 +26,41 @@ static int32_t CustomFuncB2(T2_UserData* pUserData) { return -pUserData->balance; } +static int32_t StaticInt32Array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +static T2_UndefStruct StaticUndefStructArray[] = { { 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 } }; + +T2_PointerStorage * createT2PointerStorage() { + T2_PointerStorage * s = calloc(1, sizeof(T2_PointerStorage)); + for(int i=0; i<10; ++i) { + s->int32PtrArray[i] = &StaticInt32Array[i]; + } + s->undefStructPtr = &StaticUndefStructArray[0]; + for(int i=0; i<10; ++i) { + s->undefStructPtrArray[i] = &StaticUndefStructArray[i]; + } + + for(int i=0; i<10; ++i) { + s->customFuncAVariantsArray[i] = ( i %2 == 0 ) ? CustomFuncA1 : CustomFuncA2; + } + for(int i=0; i<10; ++i) { + s->customFuncBVariantsArray[i] = ( i %2 == 0 ) ? CustomFuncB1 : CustomFuncB2; + } + return s; +} + +void destroyT2PointerStorage(T2_PointerStorage * s) { + assert(NULL!=s); + memset(s, 0, sizeof(T2_PointerStorage)); + free(s); +} + int Initialize(T2_InitializeOptions* Options) { Options->ProductName = calloc(100, sizeof(char)); Options->ProductVersion = calloc(100, sizeof(char)); strncpy((char*)Options->ProductName, "Product Name", 100); // yuck: nonsense-warning strncpy((char*)Options->ProductVersion, "Product Version", 100); // yuck: nonsense-warning Options->ApiVersion = 1; - - Options->Reserved1 = NULL; + Options->Reserved1 = (void*) 0x0000CAFFEEBEEFUL; Options->CustomFuncA1 = CustomFuncA1; *( (T2_CustomFuncA*) &Options->CustomFuncA2 ) = CustomFuncA2; // yuck: real yuck Options->CustomFuncB1 = CustomFuncB1; @@ -59,31 +86,21 @@ int Release(T2_InitializeOptions* Options) { Options->CustomFuncB2 = NULL; } -static int32_t StaticInt32Array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; -static T2_UndefStruct StaticUndefStructArray[] = { { 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 } }; - -T2_PointerStorage * createT2PointerStorage() { - T2_PointerStorage * s = calloc(1, sizeof(T2_PointerStorage)); - for(int i=0; i<10; ++i) { - s->int32PtrArray[i] = &StaticInt32Array[i]; - } - s->undefStructPtr = &StaticUndefStructArray[0]; - for(int i=0; i<10; ++i) { - s->undefStructPtrArray[i] = &StaticUndefStructArray[i]; - } +static T2_CallbackFunc01 t2_callback01 = NULL; +static void* t2_callback01_userparam = NULL; - for(int i=0; i<10; ++i) { - s->customFuncAVariantsArray[i] = ( i %2 == 0 ) ? CustomFuncA1 : CustomFuncA2; - } - for(int i=0; i<10; ++i) { - s->customFuncBVariantsArray[i] = ( i %2 == 0 ) ? CustomFuncB1 : CustomFuncB2; - } - return s; +void MessageCallback01(T2_CallbackFunc01 func, void* userParam) { + t2_callback01 = func; + t2_callback01_userparam = userParam; + fprintf(stderr, "XXX MessageCallback01 func %p, user %p\n", func, userParam); + fflush(NULL); } -void destroyT2PointerStorage(T2_PointerStorage * s) { - assert(NULL!=s); - memset(s, 0, sizeof(T2_PointerStorage)); - free(s); +void InjectMessageCallback01(size_t id, const char* msg) { + if( NULL != t2_callback01 ) { + fprintf(stderr, "XXX InjectMessageCallback01 func %p, user %p\n", t2_callback01, t2_callback01_userparam); + fflush(NULL); + (*t2_callback01)(id, msg, t2_callback01_userparam); + } } diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg index 77a8433..94ea0af 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg @@ -43,12 +43,36 @@ ReturnsStringOnly T2_InitializeOptions.ProductVersion # ReturnedArrayLength T2_InitializeOptions.OverrideThreadAffinity 1 MaxOneElement T2_InitializeOptions.OverrideThreadAffinity -# typedef int32_t ( * T2_CallbackFunc)(size_t id, const char* msg, void* userParam); -# void InjectMessageCallback(size_t id, const char* msg); -ArgumentIsString T2_CallbackFunc 1 -ArgumentIsString InjectMessageCallback 1 -# Define a JavaCallback, enacted on a function-pointer argument `T2_CallbackFunc` and a user-param `void*` for Java Object mapping -# JavaCallbackDef T2_CallbackFunc 2 +# Begin JavaCallback. +# +# JavaCallback requires `JNI_OnLoad*(..)` and `JVMUtil_GetJNIEnv(..)` +LibraryOnLoad Bindingtest2 + +# typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); +# void MessageCallback01(T2_CallbackFunc01 cbFunc, void* usrParam); +# void InjectMessageCallback01(size_t id, const char* msg); +ArgumentIsString T2_CallbackFunc01 1 +ArgumentIsString InjectMessageCallback01 1 + +# Define a JavaCallback. +# Set JavaCallback via function `MessageCallback01` if `T2_CallbackFunc01` argument is non-null, otherwise removes the callback and associated resources. +# It uses `usrParam` as the resource-key to map to the hidden native-usrParam object, +# hence a matching 'usrParam' must be passed for setting and removal of the callback. +# +# It uses the function-pointer argument `T2_CallbackFunc01` as the callback function type +# and marks `T2_CallbackFunc01`s 3rd argument (index 2) as the mandatory user-param for Java Object mapping. +# +# Note: An explicit `isMessageCallback01Mapped(Object usrParam)` is being created to explicitly query whether `usrParam` maps to the associated resources. +JavaCallbackDef MessageCallback01 T2_CallbackFunc01 2 + +# +# End JavaCallback + +# typedef void ( * T2_CallbackFunc02)(const T2_Callback02UserType* usrParam); +# void MessageCallback02(T2_CallbackFunc02 cbFunc, const T2_Callback02UserType* usrParam); +# void InjectMessageCallback02(size_t id, const char* msg); +ArgumentIsString InjectMessageCallback02 1 +JavaCallbackDef MessageCallback02 T2_CallbackFunc02 0 CustomCCode #include "test2.h" diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h index 78bdf87..afe94a5 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h @@ -58,9 +58,24 @@ typedef struct { extern int Initialize(T2_InitializeOptions* Options); extern int Release(T2_InitializeOptions* Options); -typedef int32_t ( * T2_CallbackFunc)(size_t id, const char* msg, void* userParam); +// +// T2_CallbackFunc01 +// +typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); -void AddMessageCallback(T2_CallbackFunc func, void* userParam); -void RemoveMessageCallback(T2_CallbackFunc func, void* userParam); -void InjectMessageCallback(size_t id, const char* msg); +void MessageCallback01(T2_CallbackFunc01 cbFunc, void* usrParam); +void InjectMessageCallback01(size_t id, const char* msg); + +// +// T2_CallbackFunc02 +// +typedef struct { + int32_t ApiVersion; + void* Data; +} T2_Callback02UserType; + +typedef void ( * T2_CallbackFunc02)(const T2_Callback02UserType* usrParam); + +void MessageCallback02(T2_CallbackFunc02 cbFunc, const T2_Callback02UserType* usrParam); +void InjectMessageCallback02(size_t id, const char* msg); |