diff options
author | Sven Gothel <[email protected]> | 2023-06-29 03:50:12 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-06-29 03:50:12 +0200 |
commit | 6591f1fef419841660311bbb554aeda7b267c9a7 (patch) | |
tree | aa5dba474e78f83628e1cfa7a700797c3f848594 /src/java/com/jogamp/gluegen | |
parent | 1d66739e09899cb90888c8fe34aba339511aa656 (diff) |
GlueGen JavaCallback: 1st Working Draft: Changed 'JavaCallbackDef' config, added code generation incl. native to Java dispatch and resource management
Tested via Test4JavaCallback.java (using test2.[hc]).
Please read the GlueGen_Mapping.md as well as Test4JavaCallback.java .
+++
Some implementation details:
JavaConfiguration maps JavaCallbackDef to JavaCallback set-function and maintains a list.
JavaCallbackDef itself holds all configured details.
JavaConfiguration also maps JavaCallbackInfo to JavaCallback set-function.
JavaCallbackInfo itself holds all compile time information, as produced by JavaEmitter.beginFunctions(..).
This extends JavaCallbackDef and avoid repetetive computation for the callback-function-type and its MethodBinding,
parameter indices for the callback interface and userParam, etc.
CMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallback setter function creates a native 'UserParam' struct instance,
which holds the callback-interface-jobject, its callback-jmethodID and
the userParam-jobject for invocation of the actual JavaCallback interface method.
- To produce the C-Type -> JNI-Type conversion, An internal CMethodBindingEmitter instance
for the native-callback function binding is created inside the CMethodBindingEmitter of the callback setter method.
It is being used to map the types to JNI within the generated native callback function,
passed to the actual JavaCallback method.
JavaMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallbacl setter passes the callback-interface-object, the userParam-object and the
callback-method-signature (to have the native method retrieve the jmethodID).
- It receives the native pointer of the native `UserParam` struct instance,
which gets mapped to the userParam-object. (*TODO: Refine ownership + release*).
Diffstat (limited to 'src/java/com/jogamp/gluegen')
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 188 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaConfiguration.java | 139 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 100 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java | 66 |
4 files changed, 374 insertions, 119 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; |