diff options
Diffstat (limited to 'src/java/com/jogamp')
-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; |