summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/java/com/jogamp/gluegen/CMethodBindingEmitter.java188
-rw-r--r--src/java/com/jogamp/gluegen/JavaConfiguration.java139
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java100
-rw-r--r--src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java66
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java2
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java143
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/test2.c67
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg36
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/test2.h23
9 files changed, 609 insertions, 155 deletions
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);