diff options
-rw-r--r-- | doc/GlueGen_Mapping.html | 231 | ||||
-rw-r--r-- | doc/GlueGen_Mapping.md | 84 | ||||
-rwxr-xr-x | make/scripts/runtest.sh | 1 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 188 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaConfiguration.java | 139 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 100 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java | 66 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java | 2 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java | 143 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.c | 67 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg | 36 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.h | 23 |
12 files changed, 863 insertions, 217 deletions
diff --git a/doc/GlueGen_Mapping.html b/doc/GlueGen_Mapping.html index 9739082..02ec43f 100644 --- a/doc/GlueGen_Mapping.html +++ b/doc/GlueGen_Mapping.html @@ -426,11 +426,18 @@ <li><a href="#alignment-for-compound-data">Alignment for Compound Data</a></li> </ul></li> + <li><a href="#oo-style-api-interface-mapping">OO-Style API Interface + Mapping</a> + <ul> + <li><a href="#oo-style-mapping-settings">OO-Style Mapping + Settings</a></li> + <li><a href="#oo-style-example">OO-Style Example</a></li> + </ul></li> <li><a href="#struct-mapping">Struct Mapping</a> <ul> + <li><a href="#struct-mapping-notes">Struct Mapping Notes</a></li> <li><a href="#gluegen-struct-settings">GlueGen Struct Settings</a></li> - <li><a href="#struct-mapping-notes">Struct Mapping Notes</a></li> <li><a href="#struct-setter-pseudo-code">Struct Setter Pseudo-Code</a></li> <li><a href="#struct-java-signature-table">Struct Java Signature @@ -441,8 +448,18 @@ Support</a></li> <li><a href="#struct-function-pointer-support">Struct Function-Pointer Support</a></li> + </ul></li> <li><a href="#java-callback-from-native-c-api-support">Java Callback - from Native C-API Support</a></li> + from Native C-API Support</a> + <ul> + <li><a href="#javacallback-constraints">JavaCallback + Constraints</a></li> + </ul></li> + <li><a href="#misc-configurations">Misc Configurations</a> + <ul> + <li><a + href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad <LibraryBasename></code> + for <code>JNI_OnLoad*(..)</code> ...</a></li> </ul></li> <li><a href="#platform-header-files">Platform Header Files</a></li> <li><a href="#pre-defined-macros">Pre-Defined Macros</a></li> @@ -483,11 +500,30 @@ capable of representing all C types to represent the APIs for which it generates interfaces. It has the ability to perform significant transformations on the IR before glue code emission.</p> <p>GlueGen can produce native foreign function bindings to Java as well -as map native data structures to be fully accessible from Java including -potential calls to embedded function pointer.</p> -<p>GlueGen is also capable to bind even low-level APIs such as the Java -Native Interface (JNI) and the AWT Native Interface (JAWT) back up to -the Java programming language.</p> +as <a href="#struct-mapping">map native data structures</a> to be fully +accessible from Java including potential calls to <a +href="#struct-function-pointer-support">embedded function +pointer</a>.</p> +<p>GlueGen supports <a +href="#java-callback-from-native-c-api-support">registering Java +callback methods</a> to receive asynchronous and off-thread native +toolkit events, where a generated native callback function dispatches +the events to Java.</p> +<p>GlueGen also supports <a +href="#oo-style-api-interface-mapping">producing an OO-Style API +mapping</a> like <a href="../../jogl/doc/uml/html/index.html">JOGL's +incremental OpenGL Profile API levels</a>.</p> +<p>GlueGen is capable to bind low-level APIs such as the Java Native +Interface (JNI) and the AWT Native Interface (JAWT) back up to the Java +programming language.</p> +<p>Further, GlueGen supports generating <code>JNI_OnLoad(..)</code> for +dynamic and <code>JNI_OnLoad_<LibraryBasename>(..)</code> for +static libraries via <a +href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad Bindingtest2</code></a>, +which also provides <code>JVMUtil_GetJNIEnv(..)</code> to resolve the +<code>JNIEnv*</code> as used by <a +href="#java-callback-from-native-c-api-support">Java callback +methods</a>.</p> <p>GlueGen utilizes <a href="https://jogamp.org/cgit/jcpp.git/about/">JCPP</a>, migrated C preprocessor written in Java.</p> @@ -930,6 +966,68 @@ Window(mingw/mingw64)</h4> +armv7l-eabi<br /> - MacOsX-32bit-gcc4<br /> ∗ Windows</p> +<h2 id="oo-style-api-interface-mapping">OO-Style API Interface +Mapping</h2> +<p>GlueGen supports producing an OO-Style API mapping like <a +href="../../jogl/doc/uml/html/index.html">JOGL's incremental OpenGL +Profile API levels</a>.</p> +<h3 id="oo-style-mapping-settings">OO-Style Mapping Settings</h3> +<ul> +<li><p><code>ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL.java</code></p> +<p>Ignore all extended interface symbols from named Java source +file.</p> +<p>The named Java source file is parsed and a list of its symbols +extracted, allowing GlueGen to ignore these in the generated interface +(here GLES3).</p> +<p>This complements <code>Extends</code> setting, see below.</p></li> +<li><p><code>Extends GLES3 GLES2</code></p> +<p>The generated interface GLES3 extends interface GLES2.</p> +<p>This complements <code>ExtendedInterfaceSymbolsIgnore</code> setting, +see above.</p></li> +<li><p><code>Implements GLES3Impl GLES3</code></p> +<p>The generated implementation GLES3Impl implements interface +GLES3.</p></li> +</ul> +<h3 id="oo-style-example">OO-Style Example</h3> +<p>Example snippet from JOGL's GLES3 interface config +<code>gl-if-es3.cfg</code></p> +<pre><code>... + +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL2ES2.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GLES2.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL2ES3.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL3ES3.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL4ES3.java +ExtendedInterfaceSymbolsIgnore ../src/jogl/classes/com/jogamp/opengl/GLBase.java + +Package com.jogamp.opengl +Style InterfaceOnly +JavaClass GLES3 +Extends GLES3 GLES2 +Extends GLES3 GL4ES3 +...</code></pre> +<p>Example snippet from JOGL's GLES3Impl implementation +<code>gl-es3-impl.cfg</code></p> +<pre><code>... +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL2ES2.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GLES2.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL2ES3.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL3ES3.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GL4ES3.java +ExtendedInterfaceSymbolsIgnore ../build-temp/gensrc/classes/com/jogamp/opengl/GLES3.java +ExtendedInterfaceSymbolsIgnore ../src/jogl/classes/com/jogamp/opengl/GLBase.java + +Style ImplOnly +ImplPackage jogamp.opengl.es3 +ImplJavaClass GLES3Impl +Implements GLES3Impl GLES2 +Implements GLES3Impl GLES3 +...</code></pre> +<p>Above produces the GLES3 interface and its implementation as visible +in JOGL's UML document <a +href="../../jogl/doc/uml/html/index.html">about OpenGL Profiles</a>.</p> <h2 id="struct-mapping">Struct Mapping</h2> <p>A <em>Struct</em> is a C compound type declaration, which can be mapped to a Java class.</p> @@ -1826,43 +1924,104 @@ StructPackage T2_InitializeOptions com.jogamp.gluegen.test.junit.generation</cod /** Interface to C language function: <br> <code>int32_t CustomFuncB1(T2_UserData * pUserData)</code><br> */ public final int CustomFuncB1(T2_UserData pUserData) { .. } </code></pre> -<h3 id="java-callback-from-native-c-api-support">Java Callback from -Native C-API Support</h3> -<p>GlueGen supports registering Java callback methods to native C-API -functions in the form:</p> -<pre><code>typedef int32_t ( * T_CallbackFunc)(size_t id, const char* msg, void* userParam); - -void AddMessageCallback(T_CallbackFunc func, void* userParam); -void RemoveMessageCallback(T_CallbackFunc func, void* userParam); -void InjectMessageCallback(size_t id, const char* msg);</code></pre> +<h2 id="java-callback-from-native-c-api-support">Java Callback from +Native C-API Support</h2> +<p>GlueGen supports registering Java callback methods to receive +asynchronous and off-thread native toolkit events, where a generated +native callback function dispatches the events to Java.</p> +<p>C-API header snippet:</p> +<pre><code>typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); + +/** Sets the given `cbFunc` and associated `usrParam` as the callback. Passing NULL for `func` _and_ same `usrParam` removes the callback and its associated resources. */ +void MessageCallback01(T2_CallbackFunc01 cbFunc, void* usrParam); + +void InjectMessageCallback01(size_t id, const char* msg);</code></pre> <p>and the following GlueGen configuration</p> -<pre><code>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</code></pre> +<pre><code># JavaCallback requires `JNI_OnLoad*(..)` and `JVMUtil_GetJNIEnv(..)` +LibraryOnLoad Bindingtest2 + +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</code></pre> +<p>Note that <a +href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad Bindingtest2</code></a> +must be specified in exactly one native code-unit. It provides code to +allow the generated native callback-function to attach the current +thread to the <code>JavaVM*</code> generating a new +<code>JNIEnv*</code>in daemon mode - or just to retrieve the thread's +<code>JNIEnv*</code>, if already attached to the +<code>JavaVM*</code>.</p> <p>This will lead to the following result</p> <pre><code>public interface Bindingtest2 { - /** JavaCallback interface: T2_CallbackFunc -> int32_t (*T2_CallbackFunc)(size_t id, const char * msg, void * userParam) */ - public static interface T2_CallbackFunc { - /** Interface to C language function: <br> <code>int32_t callback(size_t id, const char * msg, void * userParam)</code><br>Alias for: <code>T2_CallbackFunc</code> */ - public int callback(long id, String msg, Object userParam); + /** JavaCallback interface: T2_CallbackFunc01 -> void (*T2_CallbackFunc01)(size_t id, const char * msg, void * usrParam) */ + public static interface T2_CallbackFunc01 { + /** Interface to C language function: <br> <code>void callback(size_t id, const char * msg, void * usrParam)</code><br>Alias for: <code>T2_CallbackFunc01</code> */ + public void callback(long id, String msg, Object usrParam); } ... + + /** Entry point (through function pointer) to C language function: <br> <code>void MessageCallback01(T2_CallbackFunc01 cbFunc, void * usrParam)</code><br> */ + public void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam); + + public boolean isMessageCallback01Mapped(final Object usrParam); - /** Entry point (through function pointer) to C language function: <br> <code>void AddMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam)</code><br> */ - public void AddMessageCallback(T2_CallbackFunc func, Object userParam); - - /** Entry point (through function pointer) to C language function: <br> <code>void RemoveMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam)</code><br> */ - public void RemoveMessageCallback(T2_CallbackFunc func, Object userParam); - - /** Entry point (through function pointer) to C language function: <br> <code>void InjectMessageCallback(size_t id, const char * msg)</code><br> */ - public void InjectMessageCallback(long id, String msg); -</code></pre> -<p><em>TODO: Work in progress</em></p> -<h4 id="example-1">Example</h4> + /** Entry point (through function pointer) to C language function: <br> <code>void InjectMessageCallback01(size_t id, const char * msg)</code><br> */ + public void InjectMessageCallback01(long id, String msg);</code></pre> +<h3 id="javacallback-constraints">JavaCallback Constraints</h3> +<p>Please consider the following <em>currently enabled</em> constraints +using <code>JavaCallbackDef</code></p> +<ul> +<li>Only one interface callback-method binding is allowed for a native +callback function, e.g. <code>T2_CallbackFunc01</code> (see above).</li> +<li>The native callback function can only return no-value, i.e. +<code>void</code>, or a primitive type. Usually <code>void</code> is +being used in toolkit APIs.</li> +<li>The native callback function argument types must be able to be +mapped to JNI Java types as supported for return values of all native +functions, the same code path is being used within +<code>CMethodBindingEmitter.emitBodyMapCToJNIType(..)</code>.</li> +<li>To remove a JavaCallback the specified and mapped setter function, +e.g. <code>MessageCallback01</code>, must be called with +<code>null</code> for the callback interface but the very same +<code>userParam</code> instance as previously called to set the +callback.</li> +<li>Exactly one native code-unit for the library must specify <a +href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad libraryBasename</code></a></li> +<li>...</li> +</ul> +<p><em>TODO: Enhance documentation</em></p> +<h2 id="misc-configurations">Misc Configurations</h2> +<h3 +id="libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad <LibraryBasename></code> +for <code>JNI_OnLoad*(..)</code> ...</h3> +<p><code>LibraryOnLoad <LibraryBasename></code> generates native +JNI code <code>JNI_OnLoad(..)</code> used for dynamic libraries, +<code>JNI_OnLoad_<LibraryBasename>(..)</code> used for static +libraries, <code>JVMUtil_GetJNIEnv(..)</code> and the instance of +<code>JavaVM* <LibraryBasename>_jvmHandle</code>.</p> +<p>The <code>JNI_OnLoad*(..)</code> methods set the +<code>JavaVM* <LibraryBasename>_jvmHandle</code>, which in turn is +utilized by <code>JVMUtil_GetJNIEnv(..)</code> to attach a new thread to +the <code>JavaVM*</code> generating a new <code>JNIEnv*</code>in daemon +mode - or just to retrieve the thread's <code>JNIEnv*</code>, if already +attached to the <code>JavaVM*</code>.</p> +<p>The <code>LibraryBasename</code> parameter is used to generate the +<code>JNI_OnLoad_<LibraryBasename>(..)</code> variant for +statically linked libraries. <code>JNI_OnLoad(..)</code>, +<code>JNI_OnLoad_<LibraryBasename>(..)</code> and +<code>JVMUtil_GetJNIEnv(..)</code></p> <h2 id="platform-header-files">Platform Header Files</h2> <p>GlueGen provides convenient platform headers,<br /> which can be included in your C header files for native compilation and diff --git a/doc/GlueGen_Mapping.md b/doc/GlueGen_Mapping.md index ee23845..738c529 100644 --- a/doc/GlueGen_Mapping.md +++ b/doc/GlueGen_Mapping.md @@ -31,6 +31,10 @@ GlueGen can produce native foreign function bindings to Java as well as [map native data structures](#struct-mapping) to be fully accessible from Java including potential calls to [embedded function pointer](#struct-function-pointer-support). +GlueGen supports [registering Java callback methods](#java-callback-from-native-c-api-support) +to receive asynchronous and off-thread native toolkit events, +where a generated native callback function dispatches the events to Java. + GlueGen also supports [producing an OO-Style API mapping](#oo-style-api-interface-mapping) like [JOGL's incremental OpenGL Profile API levels](../../jogl/doc/uml/html/index.html). GlueGen is capable to bind low-level APIs such as the Java Native Interface (JNI) and @@ -752,49 +756,77 @@ and similar to `T2_CustomFuncB customFuncB1` public final int CustomFuncB1(T2_UserData pUserData) { .. } ``` -### Java Callback from Native C-API Support -GlueGen supports registering Java callback methods to native C-API functions in the form: -``` -typedef int32_t ( * T_CallbackFunc)(size_t id, const char* msg, void* userParam); +## Java Callback from Native C-API Support +GlueGen supports registering Java callback methods +to receive asynchronous and off-thread native toolkit events, +where a generated native callback function dispatches the events to Java. -void AddMessageCallback(T_CallbackFunc func, void* userParam); -void RemoveMessageCallback(T_CallbackFunc func, void* userParam); -void InjectMessageCallback(size_t id, const char* msg); +C-API header snippet: ``` +typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); -and the following GlueGen configuration +/** Sets the given `cbFunc` and associated `usrParam` as the callback. Passing NULL for `func` _and_ same `usrParam` removes the callback and its associated resources. */ +void MessageCallback01(T2_CallbackFunc01 cbFunc, void* usrParam); + +void InjectMessageCallback01(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 +and the following GlueGen configuration ``` +# JavaCallback requires `JNI_OnLoad*(..)` and `JVMUtil_GetJNIEnv(..)` +LibraryOnLoad Bindingtest2 + +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 +``` +Note that [`LibraryOnLoad Bindingtest2`](#libraryonload-librarybasename-for-jni_onload-) must be specified in exactly one native code-unit. +It provides code to allow the generated native callback-function to attach the current thread to the `JavaVM*` generating a new `JNIEnv*`in daemon mode - +or just to retrieve the thread's `JNIEnv*`, if already attached to the `JavaVM*`. This will lead to the following result ``` public interface Bindingtest2 { - /** JavaCallback interface: T2_CallbackFunc -> int32_t (*T2_CallbackFunc)(size_t id, const char * msg, void * userParam) */ - public static interface T2_CallbackFunc { - /** Interface to C language function: <br> <code>int32_t callback(size_t id, const char * msg, void * userParam)</code><br>Alias for: <code>T2_CallbackFunc</code> */ - public int callback(long id, String msg, Object userParam); + /** JavaCallback interface: T2_CallbackFunc01 -> void (*T2_CallbackFunc01)(size_t id, const char * msg, void * usrParam) */ + public static interface T2_CallbackFunc01 { + /** Interface to C language function: <br> <code>void callback(size_t id, const char * msg, void * usrParam)</code><br>Alias for: <code>T2_CallbackFunc01</code> */ + public void callback(long id, String msg, Object usrParam); } ... + + /** Entry point (through function pointer) to C language function: <br> <code>void MessageCallback01(T2_CallbackFunc01 cbFunc, void * usrParam)</code><br> */ + public void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam); + + public boolean isMessageCallback01Mapped(final Object usrParam); - /** Entry point (through function pointer) to C language function: <br> <code>void AddMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam)</code><br> */ - public void AddMessageCallback(T2_CallbackFunc func, Object userParam); - - /** Entry point (through function pointer) to C language function: <br> <code>void RemoveMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam)</code><br> */ - public void RemoveMessageCallback(T2_CallbackFunc func, Object userParam); - - /** Entry point (through function pointer) to C language function: <br> <code>void InjectMessageCallback(size_t id, const char * msg)</code><br> */ - public void InjectMessageCallback(long id, String msg); - + /** Entry point (through function pointer) to C language function: <br> <code>void InjectMessageCallback01(size_t id, const char * msg)</code><br> */ + public void InjectMessageCallback01(long id, String msg); ``` -*TODO: Work in progress* +### JavaCallback Constraints +Please consider the following *currently enabled* constraints using `JavaCallbackDef` +- Only one interface callback-method binding is allowed for a native callback function, e.g. `T2_CallbackFunc01` (see above). +- The native callback function can only return no-value, i.e. `void`, or a primitive type. Usually `void` is being used in toolkit APIs. +- The native callback function argument types must be able to be mapped to JNI Java types as supported for return values of all native functions, + the same code path is being used within `CMethodBindingEmitter.emitBodyMapCToJNIType(..)`. +- To remove a JavaCallback the specified and mapped setter function, e.g. `MessageCallback01`, must be called with `null` for the callback interface + but the very same `userParam` instance as previously called to set the callback. +- Exactly one native code-unit for the library must specify [`LibraryOnLoad libraryBasename`](#libraryonload-librarybasename-for-jni_onload-) +- ... + +*TODO: Enhance documentation* ## Misc Configurations diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh index 40cd4c5..e20c09e 100755 --- a/make/scripts/runtest.sh +++ b/make/scripts/runtest.sh @@ -154,6 +154,7 @@ function onetest() { onetest com.jogamp.gluegen.test.junit.generation.Test1p2DynamicLibraryBundle 2>&1 | tee -a $LOG onetest com.jogamp.gluegen.test.junit.generation.Test2FuncPtr 2>&1 | tee -a $LOG onetest com.jogamp.gluegen.test.junit.generation.Test3PtrStorage 2>&1 | tee -a $LOG +onetest com.jogamp.gluegen.test.junit.generation.Test4JavaCallback 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.structgen.TestStructGen01 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.structgen.TestStructGen02 2>&1 | tee -a $LOG diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index d0e0c45..d802ee7 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -40,11 +40,14 @@ package com.jogamp.gluegen; +import static java.util.logging.Level.INFO; + import java.io.PrintWriter; import java.text.MessageFormat; import java.util.List; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; import com.jogamp.gluegen.Logging.LoggerIf; import com.jogamp.gluegen.cgram.types.ArrayType; import com.jogamp.gluegen.cgram.types.FunctionSymbol; @@ -113,6 +116,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { // We need this in order to compute sizes of certain types protected MachineDataInfo machDesc; + private final JavaCallbackInfo javaCallback; + private final String jcbNativeBasename; + private final CMethodBindingEmitter jcbCMethodEmitter; + /** * Constructs an emitter for the specified binding, and sets a default * comment emitter that will emit the signature of the C function that is @@ -145,6 +152,18 @@ public class CMethodBindingEmitter extends FunctionEmitter { this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; this.machDesc = machDesc; + javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); + if( null != javaCallback ) { + jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.simpleCbClazzName.replace("_", "") ); + jcbCMethodEmitter = new CMethodBindingEmitter(javaCallback.cbFuncBinding, + unit, javaPackageName, javaClassName, isOverloadedBinding, + isJavaMethodStatic, forImplementingMethodCall, + forIndirectBufferAndArrayImplementation, machDesc, configuration); + } else { + jcbNativeBasename = null; + jcbCMethodEmitter = null; + } + setCommentEmitter(defaultCommentEmitter); } @@ -308,10 +327,98 @@ public class CMethodBindingEmitter extends FunctionEmitter { @Override protected void emitReturnType() { + if( null != javaCallback ) { + LOG.log(INFO, "BindCFunc.R.JavaCallback: {0}: {1}", binding.getName(), javaCallback); + final String userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.userParamIdx); + final Type cReturnType = javaCallback.cbFuncBinding.getCReturnType(); + final JavaType jretType = javaCallback.cbFuncBinding.getJavaReturnType(); + unit.emitln("typedef struct {"); + unit.emitln(" jobject cbFunc;"); + unit.emitln(" jmethodID cbMethodID;"); + unit.emitln(" jobject userParam;"); + unit.emitln("} T_"+jcbNativeBasename+";"); + unit.emitln(); + // javaCallback.cbFuncCEmitter.emitSignature(); + unit.emit("static "+cReturnType.getCName()+" func"+jcbNativeBasename+"("); + // javaCallback.cbFuncCEmitter.emitArguments(); + unit.emit(javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), null).toString()); + unit.emitln(") {"); + // javaCallback.cbFuncCEmitter.emitBody(); + { + unit.emitln(" JNIEnv* env = JVMUtil_GetJNIEnv();"); + unit.emitln(" if( NULL == env ) {"); + if( !cReturnType.isVoid() ) { + unit.emitln(" return 0;"); + } else { + unit.emitln(" return;"); + } + unit.emitln(" }"); + // javaCallback.cbFuncCEmitter.emitBodyVariableDeclarations(); + // javaCallback.cbFuncCEmitter.emitBodyUserVariableDeclarations(); + // javaCallback.cbFuncCEmitter.emitBodyVariablePreCallSetup(); + jcbCMethodEmitter.emitJavaCallbackBodyCToJavaPreCall(javaCallback); + + // javaCallback.cbFuncCEmitter.emitBodyCallCFunction(); + unit.emitln(" T_"+jcbNativeBasename+"* cb = (T_"+jcbNativeBasename+"*) "+userParamArgName+";"); + unit.emitln(" // C Params: "+javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), null).toString()); + unit.emitln(" // J Params: "+javaCallback.cbFuncBinding.getJavaParameterList(new StringBuilder()).toString()); + // unit.emitln(" fprintf(stderr, \"YYY Callback01 user %p, id %ld, msg %s\\n\", cb, id, msg);"); + + if( !cReturnType.isVoid() ) { + unit.emit(" "+cReturnType.getCName()+" _res = ("+cReturnType.getCName()+") "); + } else { + unit.emit(" "); + } + unit.emit("(*env)->Call" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cb->cbFunc, cb->cbMethodID, "); + // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); + if( 0 < jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback) ) { + unit.emit(", "); + } + unit.emitln("cb->userParam);"); + + // javaCallback.cbFuncCEmitter.emitBodyUserVariableAssignments(); + // javaCallback.cbFuncCEmitter.emitBodyVariablePostCallCleanup(); + // javaCallback.cbFuncCEmitter.emitBodyMapCToJNIType(-1 /* return value */, true /* addLocalVar */) + if( !cReturnType.isVoid() ) { + unit.emitln(" return _res;"); + } + } + unit.emitln("}"); + unit.emitln(); + } unit.emit("JNIEXPORT "); unit.emit(binding.getJavaReturnType().jniTypeName()); unit.emit(" JNICALL"); } + /* pp */ int emitJavaCallbackBodyCToJavaPreCall(final JavaCallbackInfo jcbi) { + int count = 0; + for (int i = 0; i < binding.getNumArguments(); i++) { + if( i == jcbi.userParamIdx ) { + continue; + } + if( emitBodyMapCToJNIType(i, true /* addLocalVar */) ) { + ++count; + } + } + return count; + } + /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi) { + int count = 0; + boolean needsComma = false; + for (int i = 0; i < binding.getNumArguments(); i++) { + if( i == jcbi.userParamIdx ) { + continue; + } + if (needsComma) { + unit.emit(", "); + needsComma = false; + } + unit.emit( binding.getArgumentName(i) + "_jni" ); + needsComma = true; + ++count; + } + return count; + } @Override protected void emitName() { @@ -385,6 +492,15 @@ public class CMethodBindingEmitter extends FunctionEmitter { byteOffsetArrayArgName(i)); } } + + final JavaCallbackInfo jcb = this.javaCallback; + if( null != jcb ) { + LOG.log(INFO, "BindCFunc.A.JavaCallback: {0}: {1}", binding.getName(), jcb); + unit.emit(", jstring jcallbackSignature, jlongArray jnativeUserParam"); + numEmitted+=2; + } else { + LOG.log(INFO, "BindCFunc.JavaCallback: {0}: NONE", binding.getName()); + } return numEmitted; } @@ -396,6 +512,50 @@ public class CMethodBindingEmitter extends FunctionEmitter { emitBodyVariableDeclarations(); emitBodyUserVariableDeclarations(); emitBodyVariablePreCallSetup(); + final JavaCallbackInfo jcb = this.javaCallback; + if( null != jcb ) { + LOG.log(INFO, "BindCFunc.B.JavaCallback: {0}: {1}", binding.getName(), jcb); + final String cbFuncArgName = binding.getArgumentName(jcb.setFuncCBParamIdx); + final String userParamArgName = binding.getArgumentName(jcb.setFuncUserParamIdx); + final String nativeCBFuncVarName = cbFuncArgName+"_native"; + final String nativeUserParamVarName = userParamArgName+"_native"; + unit.emitln(); + unit.emitln(" // JavaCallback handling"); + unit.emitln(" "+jcb.cbFuncTypeName+" "+nativeCBFuncVarName+";"); + unit.emitln(" T_"+jcbNativeBasename+"* "+nativeUserParamVarName+";"); + // unit.emit(", jstring jcallbackSignature, jlongArray jnativeUserParam"); + unit.emitln(" if( NULL == jnativeUserParam ) { (*env)->FatalError(env, \"Null jnativeUserParam in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" const size_t jnativeUserParam_size = (*env)->GetArrayLength(env, jnativeUserParam);"); + unit.emitln(" if( 1 > jnativeUserParam_size ) { (*env)->FatalError(env, \"nativeUserParam size < 1 in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" if( NULL != "+cbFuncArgName+" ) {"); + unit.emitln(" if( NULL == "+userParamArgName+" ) { (*env)->FatalError(env, \"Null "+userParamArgName+" in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" "+nativeUserParamVarName+" = (T_"+jcbNativeBasename+"*) calloc(1, sizeof(T_"+jcbNativeBasename+"));"); + unit.emitln(" if( NULL == "+nativeUserParamVarName+" ) { (*env)->FatalError(env, \"Can't alloc "+nativeUserParamVarName+" in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" "+nativeUserParamVarName+"->cbFunc = (*env)->NewGlobalRef(env, "+cbFuncArgName+");"); + unit.emitln(" if( NULL == "+nativeUserParamVarName+"->cbFunc ) { (*env)->FatalError(env, \"Failed NewGlobalRef(func) in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" "+nativeUserParamVarName+"->userParam = (*env)->NewGlobalRef(env, "+userParamArgName+");"); + unit.emitln(" if( NULL == "+nativeUserParamVarName+"->userParam ) { (*env)->FatalError(env, \"Failed NewGlobalRef(userParam) in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" {"); + unit.emitln(" jclass cbClazz = (*env)->GetObjectClass(env, "+nativeUserParamVarName+"->cbFunc);"); + unit.emitln(" if( NULL == cbClazz ) { (*env)->FatalError(env, \"Failed GetObjectClass in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" const char* callbackSignature = (*env)->GetStringUTFChars(env, jcallbackSignature, (jboolean*)NULL);"); + unit.emitln(" if( NULL == callbackSignature ) { (*env)->FatalError(env, \"Failed callbackSignature in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" "+nativeUserParamVarName+"->cbMethodID = (*env)->GetMethodID(env, cbClazz, \"callback\", callbackSignature);"); + unit.emitln(" (*env)->ReleaseStringUTFChars(env, jcallbackSignature, callbackSignature);"); + unit.emitln(" if( NULL == "+nativeUserParamVarName+"->cbMethodID ) { (*env)->FatalError(env, \"Failed GetMethodID in '"+jcbNativeBasename+"'\"); }"); + unit.emitln(" }"); + unit.emitln(" "+nativeCBFuncVarName+" = func"+jcbNativeBasename+";"); + unit.emitln(" } else {"); + unit.emitln(" "+nativeCBFuncVarName+" = NULL;"); + unit.emitln(" "+nativeUserParamVarName+" = NULL;"); + unit.emitln(" }"); + unit.emitln(" {"); + unit.emitln(" jlong v = (jlong) (intptr_t) "+nativeUserParamVarName+";"); + unit.emitln(" (*env)->SetLongArrayRegion(env, jnativeUserParam, 0, (jsize)1, &v);"); + // unit.emitln(" fprintf(stderr, \"YYY MessageCallback01 user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); + unit.emitln(" }"); + unit.emitln(); + } emitBodyCallCFunction(); emitBodyUserVariableAssignments(); emitBodyVariablePostCallCleanup(); @@ -404,6 +564,19 @@ public class CMethodBindingEmitter extends FunctionEmitter { } unit.emitln("}"); unit.emitln(); + if( null != jcb ) { + unit.emitln("JNIEXPORT void JNICALL"); + unit.emit(JavaEmitter.getJNIMethodNamePrefix(getJavaPackageName(), getJavaClassName())); + unit.emitln("_release"+getInterfaceName()+"Impl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); + unit.emitln(" T_"+jcbNativeBasename+"* nativeUserParam = (T_"+jcbNativeBasename+"*) (intptr_t) jnativeUserParam;"); + unit.emitln(" if( NULL != nativeUserParam ) {"); + unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->cbFunc);"); + unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->userParam);"); + unit.emitln(" free(nativeUserParam);"); + unit.emitln(" }"); + unit.emitln("}"); + unit.emitln(); + } } protected void emitBodyVariableDeclarations() { @@ -953,6 +1126,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { } else { if (javaArgType.isString()) { unit.emit(STRING_CHARS_PREFIX); } unit.emit(binding.getArgumentName(i)); + if( null != this.javaCallback && + ( i == this.javaCallback.setFuncCBParamIdx || i == this.javaCallback.setFuncUserParamIdx ) ) { + unit.emit("_native"); + } } } } @@ -1212,8 +1389,15 @@ public class CMethodBindingEmitter extends FunctionEmitter { final StringBuilder buf = new StringBuilder(); buf.append(JavaEmitter.jniMangle(getImplName())); buf.append(getImplSuffix()); - buf.append("__"); - return getJNIMangledArgs(binding, forIndirectBufferAndArrayImplementation, buf).toString(); + if( null == this.javaCallback ) { + buf.append("__"); + getJNIMangledArgs(binding, forIndirectBufferAndArrayImplementation, buf); + if( null != this.javaCallback ) { + getJNIMangledArg(String.class, buf, false); // to account for the additional 'jstring jcallbackSignature' parameter + getJNIMangledArg(long[].class, buf, false); // to account for the additional 'long[] nativeUserParam' parameter + } + } + return buf.toString(); } /** diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 3faddcd..17ac547 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -149,8 +149,26 @@ public class JavaConfiguration { * converted to String args; value is List of Integer argument indices */ private final Map<String, List<Integer>> argumentsAreString = new HashMap<String, List<Integer>>(); - private final Map<String, Integer> javaCallbackUserParams = new HashMap<String, Integer>(); - private final List<String> javaCallbackList = new ArrayList<String>(); + + /** JavaCallback configuration definition (static) */ + public static class JavaCallbackDef { + final String setFuncName; + final String cbFuncTypeName; + final int userParamIdx; + JavaCallbackDef(final String setFuncName, final String cbFuncTypeName, final int userParamIdx) { + this.setFuncName = setFuncName; + this.cbFuncTypeName = cbFuncTypeName; + this.userParamIdx = userParamIdx; + } + @Override + public String toString() { + return String.format("JavaCallbackDef[set %s, cb %s, userParamIdx %d]", + setFuncName, cbFuncTypeName, userParamIdx); + } + } + private final List<JavaCallbackDef> javaCallbackList = new ArrayList<JavaCallbackDef>(); + private final Map<String, JavaCallbackDef> javaCallbackSetFuncToDef = new HashMap<String, JavaCallbackDef>(); + private final Set<String> extendedIntfSymbolsIgnore = new HashSet<String>(); private final Set<String> extendedIntfSymbolsOnly = new HashSet<String>(); private final Set<String> extendedImplSymbolsIgnore = new HashSet<String>(); @@ -539,30 +557,21 @@ public class JavaConfiguration { return returnsStringOnly.contains(functionName); } - public List<String> getJavaCallbackList() { + /** Returns the list of all configured JavaCallback definitions. */ + public List<JavaCallbackDef> getJavaCallbackList() { return javaCallbackList; } - /** Returns an <code>Integer</code> index of the <code>void*</code> - user-param argument that should be converted to <code>Object</code>s for the Java Callback. Returns null if there are no - such hints for the given function alias symbol. */ - public boolean isJavaCallback(final AliasedSymbol symbol) { - return -2 < javaCallbackUserParamIdx(symbol); - } - - /** Returns an <code>Integer</code> index of the <code>void*</code> - user-param argument that should be converted to <code>Object</code>s for the Java Callback. Returns -2 if there are no - such hints for the given function alias symbol. */ - public int javaCallbackUserParamIdx(final AliasedSymbol symbol) { + /** Returns the configured JavaCallback definition mapped to the JavaCallback-Set-Function name. */ + public JavaCallbackDef javaCallbackSetFuncToDef(final AliasedSymbol symbol) { final String name = symbol.getName(); final Set<String> aliases = symbol.getAliasedNames(); - Integer res = javaCallbackUserParams.get(name); + JavaCallbackDef res = javaCallbackSetFuncToDef.get(name); if( null == res ) { - res = oneInMap(javaCallbackUserParams, aliases); + res = oneInMap(javaCallbackSetFuncToDef, aliases); } - LOG.log(INFO, getASTLocusTag(symbol), "JavaCallbackDef: {0} -> {1}", symbol, res); - return null != res ? res.intValue() : -2; + return res; } /** @@ -1615,15 +1624,12 @@ public class JavaConfiguration { protected void readJavaCallbackDef(final StringTokenizer tok, final String filename, final int lineNo) { try { - final String name = tok.nextToken(); - final Integer idx; - if( tok.hasMoreTokens() ) { - idx = Integer.valueOf(tok.nextToken()); - } else { - idx = Integer.valueOf(-2); - } - javaCallbackUserParams.put(name, idx); - javaCallbackList.add(name); + final String setFuncName = tok.nextToken(); + final String cbFuncTypeName = tok.nextToken(); + final Integer userParamIdx = Integer.valueOf(tok.nextToken()); + final JavaCallbackDef jcd = new JavaCallbackDef(setFuncName, cbFuncTypeName, userParamIdx); + javaCallbackList.add(jcd); + javaCallbackSetFuncToDef.put(setFuncName, jcd); } catch (final NoSuchElementException e) { throw new RuntimeException("Error parsing \"JavaCallbackDef\" command at line " + lineNo + " in file \"" + filename + "\"", e); @@ -2233,53 +2239,76 @@ public class JavaConfiguration { } /** - * JavaCallback information, produced by {@link JavaEmitter#beginFunctions(TypeDictionary, TypeDictionary, Map)} + * JavaCallback compile time information, produced by {@link JavaEmitter#beginFunctions(TypeDictionary, TypeDictionary, Map)} * from {@link Type#isFunctionPointer() function-pointer} {@link Type}s mapped to {@link JavaConfiguration#getJavaCallbackList()} names via {@link TypeDictionary} (typedef). * @see JavaConfiguration#funcPtrTypeToJavaCallbackMap - * @see JavaConfiguration#bindingToJavaCallbackMap + * @see JavaConfiguration#setFuncToJavaCallbackMap */ - public static class JavaCallback { - final String funcName; - final String simpleClazzName; - final String fqClazzName; - final String methodSignature; - final FunctionType func; + public static class JavaCallbackInfo { + final String setFuncName; + final String cbFuncTypeName; + final String simpleCbClazzName; + final String fqCbClazzName; + final String cbMethodSignature; + final FunctionType cbFuncType; + final MethodBinding cbFuncBinding; final int userParamIdx; final Type userParamType; final String userParamName; - - public JavaCallback(final String funcName, final String simpleClazzName, final String fqClazzName, final String methodSignature, - final FunctionType func, final int userParamIdx) { - this.funcName = funcName; - this.simpleClazzName = simpleClazzName; - this.fqClazzName = fqClazzName; - this.methodSignature = methodSignature; - this.func = func; + boolean setFuncProcessed; + int setFuncCBParamIdx; + int setFuncUserParamIdx; + + public JavaCallbackInfo(final String setFuncName, final String cbFuncTypeName, final String simpleClazzName, final String fqClazzName, final String methodSignature, + final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int userParamIdx) { + this.setFuncName = setFuncName; + this.cbFuncTypeName = cbFuncTypeName; + this.simpleCbClazzName = simpleClazzName; + this.fqCbClazzName = fqClazzName; + this.cbMethodSignature = methodSignature; + this.cbFuncType = cbFuncType; + this.cbFuncBinding = cbFuncBinding; int paramIdx = -2; Type paramType = null; String paramName = null; - if( 0 <= userParamIdx && userParamIdx < func.getNumArguments() ) { - final Type t = func.getArgumentType(userParamIdx); - if( null != t && t.isPointer() && t.getTargetType().isVoid() ) { - // OK 'void*' + if( 0 <= userParamIdx && userParamIdx < cbFuncType.getNumArguments() ) { + final Type t = cbFuncType.getArgumentType(userParamIdx); + if( null != t && t.isPointer() ) { + // OK '<something>*' paramIdx = userParamIdx; - paramName = func.getArgumentName(userParamIdx); - paramType = t; + paramName = cbFuncType.getArgumentName(userParamIdx); + paramType = t.getTargetType(); } } this.userParamIdx = paramIdx; this.userParamType = paramType; this.userParamName = paramName; + this.setFuncProcessed = false; + this.setFuncCBParamIdx = -1; + this.setFuncUserParamIdx = -1; + } + + public void setFuncProcessed(final int cbParamIdx, final int userParamIdx) { + if( !setFuncProcessed ) { + if( 0 <= cbParamIdx && 0 <= userParamIdx ) { + setFuncProcessed = true; + setFuncCBParamIdx = cbParamIdx; + setFuncUserParamIdx = userParamIdx; + } else { + setFuncCBParamIdx = -1; + setFuncUserParamIdx = -1; + } + } } @Override public String toString() { - return String.format("JavaCallback[%s, %s%s, userParam[idx %d, '%s', %s], %s]", funcName, fqClazzName, methodSignature, - userParamIdx, userParamName, userParamType.getSignature(null).toString(), func.toString(funcName, false, true)); + return String.format("JavaCallbackInfo[set %s(ok %b, cbIdx %d, upIdx %d), cb %s%s, userParam[idx %d, '%s', %s], %s]", + setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, + cbFuncTypeName, cbMethodSignature, + userParamIdx, userParamName, userParamType.getSignature(null).toString(), cbFuncType.toString(cbFuncTypeName, false, true)); } } - /** Mapped function-pointer type name to {@link JavaCallback} */ - /* pp */ final Map<String, JavaCallback> funcPtrTypeToJavaCallbackMap = new HashMap<String, JavaCallback>(); - /** Mapped binding name to {@link JavaCallback} */ - /* pp */ final Map<String, JavaCallback> bindingToJavaCallbackMap = new HashMap<String, JavaCallback>(); + /** Mapped binding name to {@link JavaCallbackInfo} */ + /* pp */ final Map<String, JavaCallbackInfo> setFuncToJavaCallbackMap = new HashMap<String, JavaCallbackInfo>(); } diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index 41c5203..7dad237 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -75,6 +75,9 @@ import com.jogamp.common.os.DynamicLookupHelper; import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.util.ArrayHashMap; import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.FunctionEmitter.EmissionModifier; +import com.jogamp.gluegen.JavaConfiguration.JavaCallbackDef; +import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; import com.jogamp.gluegen.Logging.LoggerIf; import com.jogamp.gluegen.cgram.types.AliasedSymbol; import com.jogamp.gluegen.cgram.types.ArrayType; @@ -363,13 +366,13 @@ public class JavaEmitter implements GlueEmitter { if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { javaUnit().emitln(); if( !cfg.getJavaCallbackList().isEmpty() ) { - final List<String> javaCallbacks = cfg.getJavaCallbackList(); - for(final String javaCallback : javaCallbacks) { - final Type funcPtr = typedefDictionary.get(javaCallback); + final List<JavaCallbackDef> javaCallbacks = cfg.getJavaCallbackList(); + for(final JavaCallbackDef jcbd : javaCallbacks) { + final Type funcPtr = typedefDictionary.get(jcbd.cbFuncTypeName); if( null != funcPtr && funcPtr.isFunctionPointer() ) { - generateJavaCallbackCode(javaCallback, funcPtr.getTargetFunction()); + generateJavaCallbackCode(jcbd, funcPtr.getTargetFunction()); } else { - LOG.log(WARNING, "JavaCallback '{0}' function-pointer type not available", javaCallback); + LOG.log(WARNING, "JavaCallback '{0}' function-pointer type not available", jcbd); } } } @@ -1453,27 +1456,33 @@ public class JavaEmitter implements GlueEmitter { } } - private void generateJavaCallbackCode(final String funcName, final FunctionType funcType) { + private void generateJavaCallbackCode(final JavaCallbackDef jcbd, final FunctionType funcType) { final FunctionSymbol funcSym = new FunctionSymbol("callback", funcType); - funcSym.addAliasedName(funcName); - final int userParamIdx = cfg.javaCallbackUserParamIdx(funcSym); - LOG.log(INFO, "JavaCallback: fSym {0}, userParam {1}", funcSym.getAliasedString(), userParamIdx); + funcSym.addAliasedName(jcbd.cbFuncTypeName); + LOG.log(INFO, "JavaCallback: fSym {0}, {1}", funcSym.getAliasedString(), jcbd); - final String simpleClazzName = capitalizeString(funcName); + final String simpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName); final String fqClazzName = cfg.packageName()+"."+cfg.className()+"."+simpleClazzName; final StringBuilder methodSignature = new StringBuilder(); - javaUnit.emitln(" /** JavaCallback interface: "+funcName+" -> "+funcType.toString(funcName, false, true)+" */"); + javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */"); javaUnit.emitln(" public static interface "+simpleClazzName+" {"); - generateFunctionInterfaceCode(javaUnit, funcSym, userParamIdx, methodSignature); + final List<MethodBinding> mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd.userParamIdx, methodSignature); javaUnit.emitln(" }"); javaUnit.emitln(); - - final JavaConfiguration.JavaCallback jcb = new JavaConfiguration.JavaCallback(funcName, simpleClazzName, fqClazzName, - methodSignature.toString(), funcType, userParamIdx); - cfg.funcPtrTypeToJavaCallbackMap.put(funcName, jcb); - LOG.log(INFO, "JavaCallback: Added {0}", jcb); + if( 1 != mbs.size() ) { + throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final MethodBinding mb = mbs.get(0); + if( !mb.getJavaReturnType().isVoid() && !mb.getJavaReturnType().isPrimitive() ) { + throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+ + mb.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final JavaCallbackInfo jcbi = new JavaCallbackInfo(jcbd.setFuncName, jcbd.cbFuncTypeName, simpleClazzName, fqClazzName, + methodSignature.toString(), funcType, mb, jcbd.userParamIdx); + cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi); + LOG.log(INFO, "JavaCallbackInfo: Added {0}", jcbi); } - private void generateFunctionInterfaceCode(final JavaCodeUnit javaUnit, final FunctionSymbol funcSym, final int userParamIdx, final StringBuilder methodSignature) { + private List<MethodBinding> generateFunctionInterfaceCode(final JavaCodeUnit javaUnit, final FunctionSymbol funcSym, final int userParamIdx, final StringBuilder methodSignature) { // Emit method call and associated native code MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, null, null); @@ -1527,6 +1536,7 @@ public class JavaEmitter implements GlueEmitter { emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); emitter.emit(); } + return bindings; } private void generateFunctionPointerCode(final Set<MethodBinding> methodBindingSet, @@ -2981,7 +2991,7 @@ public class JavaEmitter implements GlueEmitter { if (cfg.emitImpl()) { if( !cfg.getJavaCallbackList().isEmpty() && null == cfg.libraryOnLoadName() ) { - LOG.log(WARNING, "JavaCallback used, but no 'LibraryOnLoad' basename specified for JNI_OnLoad(..). Exactly one native code-unit for the library must specify with 'LibraryOnLoad' basename"); + LOG.log(WARNING, "JavaCallback used, but no 'LibraryOnLoad' basename specified for JNI_OnLoad(..). Exactly one native code-unit for the library must specify 'LibraryOnLoad' basename"); } cUnit().emitHeader(cfg.libraryOnLoadName(), getImplPackageName(), cfg.implClassName(), cfg.customCCode()); } @@ -2991,7 +3001,6 @@ public class JavaEmitter implements GlueEmitter { " cfg.emitImpl()=" + cfg.emitImpl() + " cfg.emitInterface()=" + cfg.emitInterface(), e); } - } /** @@ -3061,7 +3070,8 @@ public class JavaEmitter implements GlueEmitter { // converted from byte[] or short[] to String final List<JavaType> javaArgumentTypes = new ArrayList<JavaType>(); final List<Integer> stringArgIndices = cfg.stringArguments(sym); - JavaConfiguration.JavaCallback javaCallback = null; + final JavaCallbackInfo jcbi = cfg.setFuncToJavaCallbackMap.get( sym.getName() ); + int jcbiSetFuncCBParamIdx=-1, jcbiSetFuncUserParamIdx=-1; for (int i = 0; i < sym.getNumArguments(); i++) { final Type cArgType = sym.getArgumentType(i); @@ -3070,29 +3080,18 @@ public class JavaEmitter implements GlueEmitter { // System.out.println("C arg type -> \"" + cArgType + "\"" ); // System.out.println(" Java -> \"" + mappedType + "\"" ); - final boolean isJavaCallbackArg; - if( null == javaCallback ) { - final JavaConfiguration.JavaCallback jcb = cfg.funcPtrTypeToJavaCallbackMap.get( cArgType.getName() ); - if( null != jcb && null != jcb.userParamName ) { - isJavaCallbackArg = true; - javaCallback = jcb; - cfg.bindingToJavaCallbackMap.put(sym.getName(), jcb); - LOG.log(INFO, "BindFunc.JavaCallback: {0}: {1}, {2}", sym.getName(), sym.getType().toString(sym.getName(), false, true), jcb); - } else { - isJavaCallbackArg = false; - } - } else { - isJavaCallbackArg = false; - } - - if( isJavaCallbackArg ) { + if( null != jcbi && jcbi.cbFuncTypeName.equals( cArgType.getName() ) && + ( !jcbi.setFuncProcessed || i == jcbi.setFuncCBParamIdx ) ) + { // Replace JavaCallback type with generated interface name - mappedType = JavaType.createForNamedClass( javaCallback.fqClazzName ); - } else if( null != javaCallback && null != javaCallback.userParamName && - javaCallback.userParamName.equals( cArgName ) && - cArgType.isPointer() && cArgType.getTargetType().isVoid() ) + jcbiSetFuncCBParamIdx=i; + mappedType = JavaType.createForNamedClass( jcbi.fqCbClazzName ); + } else if( null != jcbi && jcbi.userParamName.equals( cArgName ) && + ( !jcbi.setFuncProcessed || i == jcbi.setFuncUserParamIdx ) && + cArgType.isPointer() && jcbi.userParamType.equals( cArgType.getTargetType() ) ) { - // Replace optional userParam argument 'void*' with Object + // Replace optional userParam argument '<userParamType>*' with Object + jcbiSetFuncUserParamIdx=i; mappedType = JavaType.forObjectClass(); } else if (stringArgIndices != null && stringArgIndices.contains(i)) { // Take into account any ArgumentIsString configuration directives that apply @@ -3124,6 +3123,10 @@ public class JavaEmitter implements GlueEmitter { javaArgumentTypes.add(mappedType); //System.out.println("During binding of [" + sym + "], added mapping from C type: " + cArgType + " to Java type: " + mappedType); } + if( null != jcbi ) { + jcbi.setFuncProcessed(jcbiSetFuncCBParamIdx, jcbiSetFuncUserParamIdx); + LOG.log(INFO, "BindFunc.JavaCallback: {0}: {1}, {2}", sym.getName(), sym.getType().toString(sym.getName(), false, true), jcbi); + } final MethodBinding mb = new MethodBinding(sym, delegationImplName, javaReturnType, javaArgumentTypes, containingType, containingCType); @@ -3272,19 +3275,6 @@ public class JavaEmitter implements GlueEmitter { } } - /** - * Converts first letter to upper case. - */ - private static String capitalizeString(final String string) { - return Character.toUpperCase(string.charAt(0)) + string.substring(1); - } - /** - * Converts first letter to lower case. - */ - private static String decapitalizeString(final String string) { - return Character.toLowerCase(string.charAt(0)) + string.substring(1); - } - private static final String optStringCharsetCode = " private static Charset _charset = StandardCharsets.UTF_8;\n" + "\n"+ diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 74e18e5..1469b4f 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -40,6 +40,7 @@ package com.jogamp.gluegen; +import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; import com.jogamp.gluegen.cgram.HeaderParser; import com.jogamp.gluegen.cgram.types.AliasedSymbol; import com.jogamp.gluegen.cgram.types.ArrayType; @@ -94,6 +95,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { private String returnedArrayLengthExpression; private boolean returnedArrayLengthExpressionOnlyForComments = false; + private final JavaCallbackInfo javaCallback; + // A suffix used to create a temporary outgoing array of Buffers to // represent an array of compound type wrappers private static final String COMPOUND_ARRAY_SUFFIX = "_buf_array_copy"; @@ -131,6 +134,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else { setCommentEmitter(defaultInterfaceCommentEmitter); } + javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); // !forImplementingMethodCall && !isInterface } @@ -152,6 +156,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { epilogue = arg.epilogue; returnedArrayLengthExpression = arg.returnedArrayLengthExpression; returnedArrayLengthExpressionOnlyForComments = arg.returnedArrayLengthExpressionOnlyForComments; + javaCallback = arg.javaCallback; } public boolean isNativeMethod() { return isNativeMethod; } @@ -411,17 +416,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } } if( hasModifier(JavaMethodBindingEmitter.NATIVE) && - null != cfg.bindingToJavaCallbackMap.get(binding.getName()) ) { + null != javaCallback ) + { if (needComma) { unit.emit(", "); } - unit.emit("String callbackSignature"); + unit.emit("String callbackSignature, long[/*1*/] nativeUserParam"); ++numEmitted; } return numEmitted; } - protected String getNativeImplMethodName() { return binding.getImplName() + ( useNIODirectOnly ? "0" : "1" ); } @@ -468,6 +473,40 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } unit.emitln(" }"); } + if( null != javaCallback && !isPrivateNativeMethod ) { + unit.emitln(); + final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); + unit.emit(" public boolean is"+getInterfaceName()+"Mapped(final Object "+userParamArgName+")"); + if( isInterface() ) { + unit.emitln(";"); + } else { + unit.emitln(" {"); + unit.emitln(" return null != "+javaCallback.cbFuncTypeName+"UsrMap.get("+userParamArgName+");"); + unit.emitln(" }"); + unit.emitln(" private final void release"+getInterfaceName()+"(final Object "+userParamArgName+") {"); + unit.emitln(" final "+javaCallback.cbFuncTypeName+"UserParam v = "+javaCallback.cbFuncTypeName+"UsrMap.remove("+userParamArgName+");"); + // unit.emitln(" System.err.println(\"ZZZ Release v \"+v+\", v.nativeParam 0x\"+Long.toHexString(null!=v?v.nativeParam:0));"); + unit.emitln(" if( null != v ) {"); + unit.emitln(" release"+getInterfaceName()+"Impl(v.nativeParam);"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(" private static class "+javaCallback.cbFuncTypeName+"UserParam {"); + unit.emitln(" @SuppressWarnings(\"unused\")"); + unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); + unit.emitln(" @SuppressWarnings(\"unused\")"); + unit.emitln(" final Object param;"); + unit.emitln(" @SuppressWarnings(\"unused\")"); + unit.emitln(" final long nativeParam;"); + unit.emitln(" "+javaCallback.cbFuncTypeName+"UserParam("+javaCallback.cbFuncTypeName+" func, Object param, long nativeParam) {"); + unit.emitln(" this.func = func;"); + unit.emitln(" this.param = param;"); + unit.emitln(" this.nativeParam = nativeParam;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(" private final java.util.Map<Object, "+javaCallback.cbFuncTypeName+"UserParam> "+javaCallback.cbFuncTypeName+"UsrMap = new java.util.HashMap<Object, "+javaCallback.cbFuncTypeName+"UserParam>();"); + unit.emitln(" private native void release"+getInterfaceName()+"Impl(long nativeUserParam);"); + } + } } protected void emitPrologueOrEpilogue(final List<String> code) { @@ -579,11 +618,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { protected void emitReturnVariableSetupAndCall(final MethodBinding binding) { - unit.emit(" "); final JavaType returnType = binding.getJavaReturnType(); boolean needsResultAssignment = false; + if( null != javaCallback ) { + unit.emitln(" final long[] nativeUserParam = { 0 };"); + } if (!returnType.isVoid()) { + unit.emit(" "); if (returnType.isCompoundTypeWrapper() || returnType.isNIOBuffer()) { unit.emitln("final ByteBuffer _res;"); @@ -612,6 +654,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { emitCall(binding); unit.emitln(); + if( null != javaCallback ) { + final String funcArgName = binding.getArgumentName(javaCallback.setFuncCBParamIdx); + final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); + // unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam \"+nativeUserParam[0]);"); + unit.emitln(" if( 0 == nativeUserParam[0] ) {"); + unit.emitln(" release"+getInterfaceName()+"("+userParamArgName+");"); + unit.emitln(" } else {"); + unit.emitln(" "+javaCallback.cbFuncTypeName+"UsrMap.put("+userParamArgName+", new "+javaCallback.cbFuncTypeName+"UserParam("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); + unit.emitln(" }"); + } + emitPostCallCleanup(binding); emitPrologueOrEpilogue(epilogue); if (needsResultAssignment) { @@ -733,12 +786,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { needComma = true; ++numArgsEmitted; } - final JavaConfiguration.JavaCallback jcb = cfg.bindingToJavaCallbackMap.get(binding.getName()); - if( null != jcb ) { + if( null != javaCallback ) { if (needComma) { unit.emit(", "); } - unit.emit("\"" + jcb.methodSignature + "\""); + unit.emit("\"" + javaCallback.cbMethodSignature + "\", nativeUserParam"); ++numArgsEmitted; } return numArgsEmitted; 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); |