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/CMethodBindingEmitter.java | |
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/CMethodBindingEmitter.java')
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 188 |
1 files changed, 186 insertions, 2 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(); } /** |