From 6591f1fef419841660311bbb554aeda7b267c9a7 Mon Sep 17 00:00:00 2001
From: Sven Gothel
Date: Thu, 29 Jun 2023 03:50:12 +0200
Subject: 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*).
---
doc/GlueGen_Mapping.html | 231 +++++++++++++++++----
doc/GlueGen_Mapping.md | 84 +++++---
make/scripts/runtest.sh | 1 +
.../com/jogamp/gluegen/CMethodBindingEmitter.java | 188 ++++++++++++++++-
src/java/com/jogamp/gluegen/JavaConfiguration.java | 139 ++++++++-----
src/java/com/jogamp/gluegen/JavaEmitter.java | 100 ++++-----
.../jogamp/gluegen/JavaMethodBindingEmitter.java | 66 +++++-
.../gluegen/test/junit/generation/BaseClass.java | 2 +-
.../test/junit/generation/Test4JavaCallback.java | 143 +++++++++++++
.../jogamp/gluegen/test/junit/generation/test2.c | 67 +++---
.../jogamp/gluegen/test/junit/generation/test2.cfg | 36 +++-
.../jogamp/gluegen/test/junit/generation/test2.h | 23 +-
12 files changed, 863 insertions(+), 217 deletions(-)
create mode 100644 src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java
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 @@
@@ -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.
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.
-
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.
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.
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.
+
Further, GlueGen supports generating JNI_OnLoad(..) for
+dynamic and JNI_OnLoad_<LibraryBasename>(..) for
+static libraries via LibraryOnLoad Bindingtest2,
+which also provides JVMUtil_GetJNIEnv(..) to resolve the
+JNIEnv* as used by Java callback
+methods.
GlueGen utilizes JCPP, migrated C
preprocessor written in Java.
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.
+
C-API header snippet:
+
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);
and the following GlueGen configuration
-
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
+
# 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
+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);
-
-
TODO: Work in progress
-
Example
+ /** 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);
+
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.
LibraryOnLoad <LibraryBasename> generates native
+JNI code JNI_OnLoad(..) used for dynamic libraries,
+JNI_OnLoad_<LibraryBasename>(..) used for static
+libraries, JVMUtil_GetJNIEnv(..) and the instance of
+JavaVM* <LibraryBasename>_jvmHandle.
+
The JNI_OnLoad*(..) methods set the
+JavaVM* <LibraryBasename>_jvmHandle, which in turn is
+utilized by JVMUtil_GetJNIEnv(..) to attach a new 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*.
+
The LibraryBasename parameter is used to generate the
+JNI_OnLoad_<LibraryBasename>(..) variant for
+statically linked libraries. JNI_OnLoad(..),
+JNI_OnLoad_<LibraryBasename>(..) and
+JVMUtil_GetJNIEnv(..)
Platform Header Files
GlueGen provides convenient platform headers,
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: int32_t callback(size_t id, const char * msg, void * userParam) Alias for: T2_CallbackFunc */
- 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: void callback(size_t id, const char * msg, void * usrParam) Alias for: T2_CallbackFunc01 */
+ public void callback(long id, String msg, Object usrParam);
}
...
+
+ /** Entry point (through function pointer) to C language function: void MessageCallback01(T2_CallbackFunc01 cbFunc, void * usrParam) */
+ public void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam);
+
+ public boolean isMessageCallback01Mapped(final Object usrParam);
- /** Entry point (through function pointer) to C language function: void AddMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam) */
- public void AddMessageCallback(T2_CallbackFunc func, Object userParam);
-
- /** Entry point (through function pointer) to C language function: void RemoveMessageCallback(int32_t (*func)(size_t id, const char * msg, void * userParam), void * userParam) */
- public void RemoveMessageCallback(T2_CallbackFunc func, Object userParam);
-
- /** Entry point (through function pointer) to C language function: void InjectMessageCallback(size_t id, const char * msg) */
- public void InjectMessageCallback(long id, String msg);
-
+ /** Entry point (through function pointer) to C language function: void InjectMessageCallback01(size_t id, const char * msg) */
+ 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> argumentsAreString = new HashMap>();
- private final Map javaCallbackUserParams = new HashMap();
- private final List javaCallbackList = new ArrayList();
+
+ /** 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 javaCallbackList = new ArrayList();
+ private final Map javaCallbackSetFuncToDef = new HashMap();
+
private final Set extendedIntfSymbolsIgnore = new HashSet();
private final Set extendedIntfSymbolsOnly = new HashSet();
private final Set extendedImplSymbolsIgnore = new HashSet();
@@ -539,30 +557,21 @@ public class JavaConfiguration {
return returnsStringOnly.contains(functionName);
}
- public List getJavaCallbackList() {
+ /** Returns the list of all configured JavaCallback definitions. */
+ public List getJavaCallbackList() {
return javaCallbackList;
}
- /** Returns an Integer index of the void*
- user-param argument that should be converted to Objects 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 Integer index of the void*
- user-param argument that should be converted to Objects 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 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 '*'
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 funcPtrTypeToJavaCallbackMap = new HashMap();
- /** Mapped binding name to {@link JavaCallback} */
- /* pp */ final Map bindingToJavaCallbackMap = new HashMap();
+ /** Mapped binding name to {@link JavaCallbackInfo} */
+ /* pp */ final Map setFuncToJavaCallbackMap = new HashMap();
}
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 javaCallbacks = cfg.getJavaCallbackList();
- for(final String javaCallback : javaCallbacks) {
- final Type funcPtr = typedefDictionary.get(javaCallback);
+ final List 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 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 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 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 javaArgumentTypes = new ArrayList();
final List 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 '*' 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