diff options
Diffstat (limited to 'src/java/com/jogamp/gluegen')
10 files changed, 1016 insertions, 768 deletions
diff --git a/src/java/com/jogamp/gluegen/CCodeUnit.java b/src/java/com/jogamp/gluegen/CCodeUnit.java index c70df79..5c0db27 100644 --- a/src/java/com/jogamp/gluegen/CCodeUnit.java +++ b/src/java/com/jogamp/gluegen/CCodeUnit.java @@ -84,11 +84,6 @@ public class CCodeUnit extends CodeUnit { emitln( getJNIOnLoadJNIEnvCode(libraryBasename) ); } - /** Emits {@link #JavaCallbackGlueDataDecl}. */ - public void emitJavaCallbackGlueDataDecl() { - emitln( JavaCallbackGlueDataDecl ); - } - @Override public String toString() { return "CCodeUnit[unit "+cUnitName+", file "+filename+"]"; } @@ -113,16 +108,6 @@ public class CCodeUnit extends CodeUnit { " return jbyteBuffer;\n"+ "}\n"; - /** JavaCallback Glue Data typedef struct */ - public static final String JavaCallbackGlueDataDecl = - "typedef struct {\n"+ - " jobject lockObj;\n"+ - " jobject cbFunc;\n"+ - " jmethodID cbMethodID;\n"+ - " jobject userParam;\n"+ - "} T_JavaCallbackGlueData;\n"+ - "\n"; - /** * Returns native JNI declarations for `JavaVM* {libraryBasename}_jvmHandle` * and `JVMUtil_GetJNIEnv(..)`. diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index 00cae13..9c7ed0b 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -113,15 +113,11 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected static final String STRING_CHARS_PREFIX = "_strchars_"; - protected static final String T_JavaCallbackGlueData = "T_JavaCallbackGlueData"; - // We need this in order to compute sizes of certain types protected MachineDataInfo machDesc; - private final JavaCallbackInfo javaCallback; - private final String jcbNativeBasename; - private final String jcbFriendlyBasename; private final CMethodBindingEmitter jcbCMethodEmitter; + private final JavaCallbackEmitter javaCallbackEmitter; /** * Constructs an emitter for the specified binding, and sets a default @@ -155,18 +151,17 @@ public class CMethodBindingEmitter extends FunctionEmitter { this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; this.machDesc = machDesc; - javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); + final JavaCallbackInfo javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); if( null != javaCallback ) { - jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.cbSimpleClazzName.replace("_", "") ); - jcbFriendlyBasename = javaCallback.setFuncName+"("+javaCallback.cbSimpleClazzName+")"; + // jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.cbSimpleClazzName.replace("_", "") ); jcbCMethodEmitter = new CMethodBindingEmitter(javaCallback.cbFuncBinding, unit, javaPackageName, javaClassName, isOverloadedBinding, isJavaMethodStatic, forImplementingMethodCall, forIndirectBufferAndArrayImplementation, machDesc, configuration); + javaCallbackEmitter = new JavaCallbackEmitter(cfg, binding, javaCallback, null); } else { - jcbNativeBasename = null; - jcbFriendlyBasename = null; jcbCMethodEmitter = null; + javaCallbackEmitter = null; } setCommentEmitter(defaultCommentEmitter); } @@ -328,145 +323,25 @@ public class CMethodBindingEmitter extends FunctionEmitter { */ public final MachineDataInfo getMachineDataInfo() { return machDesc; } - private static final boolean DEBUG_JAVACALLBACK = false; - @Override - protected void emitReturnType() { - if( null != javaCallback ) { - LOG.log(INFO, "BindCFunc.R.JavaCallback: {0}: {1}", binding.getName(), javaCallback); - final String staticCallbackName = "func"+jcbNativeBasename; - - final Type userParamType = javaCallback.cbFuncBinding.getCArgumentType(javaCallback.cbFuncUserParamIdx); - final String userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.cbFuncUserParamIdx); - final Type cReturnType = javaCallback.cbFuncBinding.getCReturnType(); - final JavaType jretType = javaCallback.cbFuncBinding.getJavaReturnType(); - unit.emitln(); - // javaCallback.cbFuncCEmitter.emitSignature(); - unit.emit("static "+cReturnType.getCName()+" "+staticCallbackName+"("); - // javaCallback.cbFuncCEmitter.emitArguments(); - unit.emit(javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), false, 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_JavaCallbackGlueData+"* cb = ("+T_JavaCallbackGlueData+"*) "+userParamArgName+";"); - unit.emitln(" // C Params: "+javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), false, null).toString()); - unit.emitln(" // J Params: "+javaCallback.cbFuncBinding.getJavaParameterList(new StringBuilder()).toString()); - - final String returnStatement; - if( !cReturnType.isVoid() ) { - unit.emit(" "+cReturnType.getCName()+" _res = 0;"); - returnStatement = "return _res;"; - } else { - returnStatement = "return;"; - } - unit.emitln(" if( NULL == cb ) { fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': NULL "+userParamArgName+", skipping!\\n\"); "+returnStatement+" }"); - unit.emitln(); - unit.emitln(" // Use-after-free of '*cb' possible up until after GetObjectRefType() check for a brief moment!"); - unit.emitln(" // Use a copy to avoid data-race between GetObjectRefType() and MonitorEnter()\");"); - unit.emitln(" jobject lockObj = cb->lockObj;"); - unit.emitln(" if( 0 == lockObj ) { fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': NULL lock, skipping!\\n\"); "+returnStatement+" }"); - unit.emitln(); - unit.emitln(" jobjectRefType refType = (*env)->GetObjectRefType(env, lockObj);"); - unit.emitln(" if( 0 == refType ) { fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': User after free(lock), skipping!\\n\"); "+returnStatement+" }"); - unit.emitln(" jint lockRes = (*env)->MonitorEnter(env, lockObj);"); - unit.emitln(" if( 0 != lockRes ) { fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': MonitorEnter failed %d, skipping!\\n\", lockRes); "+returnStatement+" }"); - unit.emitln(" // synchronized block"); - /** - * Since we have acquired the lock, in-sync w/ our Java code, cb->cbFunc and cb->userParam could not have been changed! - * - unit.emitln(" refType = (*env)->GetObjectRefType(env, cb->userParam);"); - unit.emitln(" if( 0 == refType ) {"); - unit.emitln(" fprintf(stderr, \"Info: Callback '"+staticCallbackName+"(..)': User after free(userParam), skipping!\\n\");"); - unit.emitln(" lockRes = (*env)->MonitorExit(env, cb->lockObj);"); - unit.emitln(" if( 0 != lockRes ) { fprintf(stderr, \"Info: Callback '"+staticCallbackName+"(..)': MonitorExit failed %d\\n\", lockRes); }"); - unit.emitln(" "+returnStatement); - unit.emitln(" }"); - */ - if( !cReturnType.isVoid() ) { - unit.emit(" _res = ("+cReturnType.getCName()+") "); - } else { - unit.emit(" "); - } - unit.emit("(*env)->Call" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cb->cbFunc, cb->cbMethodID, "); - // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); - jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback, "cb->userParam"); - unit.emitln(");"); - unit.emitln(" if( (*env)->ExceptionCheck(env) ) {"); - unit.emitln(" fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': Exception in Java Callback caught:\\n\");"); - unit.emitln(" (*env)->ExceptionDescribe(env);"); - unit.emitln(" (*env)->ExceptionClear(env);"); - unit.emitln(" }"); - - // javaCallback.cbFuncCEmitter.emitBodyUserVariableAssignments(); - // javaCallback.cbFuncCEmitter.emitBodyVariablePostCallCleanup(); - // javaCallback.cbFuncCEmitter.emitBodyMapCToJNIType(-1 /* return value */, true /* addLocalVar */) - - unit.emitln(" lockRes = (*env)->MonitorExit(env, cb->lockObj);"); - unit.emitln(" if( 0 != lockRes ) { fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': MonitorExit failed %d\\n\", lockRes); }"); - unit.emitln(" "+returnStatement); - } - 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.cbFuncUserParamIdx ) { - continue; - } - if( emitBodyMapCToJNIType(i, true /* addLocalVar */) ) { - ++count; - } - } - return count; - } - /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi, final String userParamVarName) { - int count = 0; - boolean needsComma = false; - for (int i = 0; i < binding.getNumArguments(); i++) { - if (needsComma) { - unit.emit(", "); - needsComma = false; - } - if( i == jcbi.cbFuncUserParamIdx ) { - unit.emit( userParamVarName ); - } else { - unit.emit( binding.getArgumentName(i) + "_jni" ); - } - needsComma = true; - ++count; - } - return count; + protected StringBuilder appendReturnType(final StringBuilder buf) { + buf.append("JNIEXPORT "); + buf.append(binding.getJavaReturnType().jniTypeName()); + buf.append(" JNICALL"); + return buf; } @Override - protected void emitName() { - unit.emitln(); // start name on new line - unit.emit(JavaEmitter.getJNIMethodNamePrefix(getJavaPackageName(), getJavaClassName())); - unit.emit("_"); + protected StringBuilder appendName(final StringBuilder buf) { + buf.append(System.lineSeparator()); // start name on new line + buf.append(JavaEmitter.getJNIMethodNamePrefix(getJavaPackageName(), getJavaClassName())); + buf.append("_"); if (isOverloadedBinding) { - unit.emit(jniMangle(binding)); + buf.append(jniMangle(binding)); } else { - unit.emit(JavaEmitter.jniMangle(getImplName())); + buf.append(JavaEmitter.jniMangle(getImplName())); } + return buf; } protected String getImplSuffix() { @@ -481,24 +356,24 @@ public class CMethodBindingEmitter extends FunctionEmitter { } @Override - protected int emitArguments() { - unit.emit("JNIEnv *env, "); + protected int appendArguments(final StringBuilder buf) { + buf.append("JNIEnv *env, "); int numEmitted = 1; // initially just the JNIEnv if (isJavaMethodStatic && !binding.hasContainingType()) { - unit.emit("jclass"); + buf.append("jclass"); } else { - unit.emit("jobject"); + buf.append("jobject"); } - unit.emit(" _unused"); + buf.append(" _unused"); ++numEmitted; if( binding.isReturnCompoundByValue() ) { - unit.emit(", jclass _clazzBuffers"); + buf.append(", jclass _clazzBuffers"); ++numEmitted; } if (binding.hasContainingType()) { // "this" argument always comes down in argument 0 as direct buffer - unit.emit(", jobject " + JavaMethodBindingEmitter.javaThisArgumentName()); + buf.append(", jobject " + JavaMethodBindingEmitter.javaThisArgumentName()); } for (int i = 0; i < binding.getNumArguments(); i++) { final JavaType javaArgType = binding.getJavaArgumentType(i); @@ -512,91 +387,47 @@ public class CMethodBindingEmitter extends FunctionEmitter { if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { continue; } - unit.emit(", "); - unit.emit(javaArgType.jniTypeName()); - unit.emit(" "); - unit.emit(binding.getArgumentName(i)); + buf.append(", "); + buf.append(javaArgType.jniTypeName()); + buf.append(" "); + buf.append(binding.getArgumentName(i)); ++numEmitted; if (javaArgType.isPrimitiveArray() || javaArgType.isNIOBuffer()) { - unit.emit(", jint " + byteOffsetArgName(i)); + buf.append(", jint " + byteOffsetArgName(i)); if(forIndirectBufferAndArrayImplementation) { - unit.emit(", jboolean " + isNIOArgName(i)); + buf.append(", jboolean " + isNIOArgName(i)); } } else if (javaArgType.isNIOBufferArray()) { - unit.emit(", jintArray " + + buf.append(", jintArray " + 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, jobject jlockObj, jlongArray jnativeUserParam"); - numEmitted+=2; - } else { - LOG.log(INFO, "BindCFunc.JavaCallback: {0}: NONE", binding.getName()); + if( null != javaCallbackEmitter ) { + numEmitted += javaCallbackEmitter.appendCAdditionalParameter(buf); } return numEmitted; } @Override + protected void emitAdditionalCode() { + if( null != javaCallbackEmitter ) { + javaCallbackEmitter.emitCAdditionalCode(unit, jcbCMethodEmitter); + } + } + + @Override protected void emitBody() { unit.emitln(" {"); // unit().emitln("printf(\" - - - - "+ getName() + getImplSuffix() +" - - - -\\n\");"); 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_JavaCallbackGlueData+"* "+nativeUserParamVarName+";"); - // unit.emit(", jstring jcallbackSignature, jobject jlockObj, jlongArray jnativeUserParam"); - unit.emitln(" if( NULL == jlockObj ) { (*env)->FatalError(env, \"Null jlockObj in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" if( NULL == jnativeUserParam ) { (*env)->FatalError(env, \"Null jnativeUserParam in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" const size_t jnativeUserParam_size = (*env)->GetArrayLength(env, jnativeUserParam);"); - unit.emitln(" if( 1 > jnativeUserParam_size ) { (*env)->FatalError(env, \"nativeUserParam size < 1 in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" if( NULL != "+cbFuncArgName+" ) {"); - unit.emitln(" if( NULL == "+userParamArgName+" ) { (*env)->FatalError(env, \"Null "+userParamArgName+" in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" "+nativeUserParamVarName+" = ("+T_JavaCallbackGlueData+"*) calloc(1, sizeof("+T_JavaCallbackGlueData+"));"); - unit.emitln(" if( NULL == "+nativeUserParamVarName+" ) { (*env)->FatalError(env, \"Can't alloc "+nativeUserParamVarName+" in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" "+nativeUserParamVarName+"->lockObj = (*env)->NewGlobalRef(env, jlockObj);"); - unit.emitln(" if( NULL == "+nativeUserParamVarName+"->lockObj ) { (*env)->FatalError(env, \"Failed NewGlobalRef(lock) in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" "+nativeUserParamVarName+"->cbFunc = (*env)->NewGlobalRef(env, "+cbFuncArgName+");"); - unit.emitln(" if( NULL == "+nativeUserParamVarName+"->cbFunc ) { (*env)->FatalError(env, \"Failed NewGlobalRef(func) in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" "+nativeUserParamVarName+"->userParam = (*env)->NewGlobalRef(env, "+userParamArgName+");"); - unit.emitln(" if( NULL == "+nativeUserParamVarName+"->userParam ) { (*env)->FatalError(env, \"Failed NewGlobalRef(userParam) in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" {"); - unit.emitln(" jclass cbClazz = (*env)->GetObjectClass(env, "+nativeUserParamVarName+"->cbFunc);"); - unit.emitln(" if( NULL == cbClazz ) { (*env)->FatalError(env, \"Failed GetObjectClass in '"+jcbFriendlyBasename+"'\"); }"); - unit.emitln(" const char* callbackSignature = (*env)->GetStringUTFChars(env, jcallbackSignature, (jboolean*)NULL);"); - unit.emitln(" if( NULL == callbackSignature ) { (*env)->FatalError(env, \"Failed callbackSignature in '"+jcbFriendlyBasename+"'\"); }"); - 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 '"+jcbFriendlyBasename+"'\"); }"); - 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);"); - if( DEBUG_JAVACALLBACK ) { - unit.emitln(" fprintf(stderr, \"YYY user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); - } - unit.emitln(" }"); - unit.emitln(); + if( null != javaCallbackEmitter ) { + javaCallbackEmitter.emitCSetFuncPreCall(unit); } emitBodyCallCFunction(); emitBodyUserVariableAssignments(); @@ -606,24 +437,6 @@ public class CMethodBindingEmitter extends FunctionEmitter { } unit.emitln("}"); unit.emitln(); - if( null != jcb ) { - final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); - unit.emitln("JNIEXPORT void JNICALL"); - unit.emit(JavaEmitter.getJNIMethodNamePrefix(getJavaPackageName(), getJavaClassName())); - unit.emitln("_release"+capIfaceName+"Impl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); - unit.emitln(" // already locked"); - unit.emitln(" "+T_JavaCallbackGlueData+"* nativeUserParam = ("+T_JavaCallbackGlueData+"*) (intptr_t) jnativeUserParam;"); - unit.emitln(" if( NULL != nativeUserParam ) {"); - unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->lockObj);"); - unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->cbFunc);"); - unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->userParam);"); - unit.emitln(" // Ensure even w/ use-after-free jobject refs are NULL and invalid to avoid accidental reuse."); - unit.emitln(" memset(nativeUserParam, 0, sizeof("+T_JavaCallbackGlueData+"));"); - unit.emitln(" free(nativeUserParam);"); - unit.emitln(" }"); - unit.emitln("}"); - unit.emitln(); - } } protected void emitBodyVariableDeclarations() { @@ -1173,9 +986,8 @@ 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"); + if( null != javaCallbackEmitter ) { + javaCallbackEmitter.emitCOptArgumentSuffix(unit, i); } } } @@ -1241,7 +1053,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { * @param addLocalVar if true, emit instantiating the local JNI variable. * @return true if a non-void result has been produced, otherwise false */ - protected boolean emitBodyMapCToJNIType(final int argIdx, final boolean addLocalVar) + public boolean emitBodyMapCToJNIType(final int argIdx, final boolean addLocalVar) { // WARNING: this code assumes that the return type has already been // typedef-resolved. @@ -1436,12 +1248,11 @@ public class CMethodBindingEmitter extends FunctionEmitter { final StringBuilder buf = new StringBuilder(); buf.append(JavaEmitter.jniMangle(getImplName())); buf.append(getImplSuffix()); - if( null == this.javaCallback ) { + if( null == javaCallbackEmitter ) { 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 + appendJNIMangledArgs(binding, forIndirectBufferAndArrayImplementation, buf); + if( null != javaCallbackEmitter ) { + javaCallbackEmitter.appendCAdditionalJNIDescriptor(buf); } } return buf.toString(); @@ -1454,13 +1265,13 @@ public class CMethodBindingEmitter extends FunctionEmitter { * @param buf * @return */ - public static StringBuilder getJNIMangledArgs(final MethodBinding binding, final boolean forIndirectBufferAndArrayImplementation, final StringBuilder buf) { + public static StringBuilder appendJNIMangledArgs(final MethodBinding binding, final boolean forIndirectBufferAndArrayImplementation, final StringBuilder buf) { if (binding.isReturnCompoundByValue()) { - getJNIMangledArg(Class.class, buf, true); + JavaType.appendJNIDescriptor(buf, Class.class, true); } if (binding.hasContainingType()) { // "this" argument always comes down in argument 0 as direct buffer - getJNIMangledArg(java.nio.ByteBuffer.class, buf, true); + JavaType.appendJNIDescriptor(buf, java.nio.ByteBuffer.class, true); } for (int i = 0; i < binding.getNumArguments(); i++) { if (binding.isArgumentThisPointer(i)) { @@ -1477,31 +1288,31 @@ public class CMethodBindingEmitter extends FunctionEmitter { } else { Class<?> c = type.getJavaClass(); if (c != null) { - getJNIMangledArg(c, buf, false); + JavaType.appendJNIDescriptor(buf, c, false); // If Buffer offset arguments were added, we need to mangle the JNI for the // extra arguments if (type.isNIOBuffer()) { - getJNIMangledArg(Integer.TYPE, buf, false); + JavaType.appendJNIDescriptor(buf, Integer.TYPE, false); if(forIndirectBufferAndArrayImplementation) { - getJNIMangledArg(Boolean.TYPE, buf, false); + JavaType.appendJNIDescriptor(buf, Boolean.TYPE, false); } } else if (type.isNIOBufferArray()) { final int[] intArrayType = new int[0]; c = intArrayType.getClass(); - getJNIMangledArg(c , buf, true); + JavaType.appendJNIDescriptor(buf, c , true); } if (type.isPrimitiveArray()) { - getJNIMangledArg(Integer.TYPE, buf, false); + JavaType.appendJNIDescriptor(buf, Integer.TYPE, false); } } else if (type.isNamedClass()) { buf.append(type.getJNIMethodDesciptor()); } else if (type.isCompoundTypeWrapper()) { // Mangle wrappers for C structs as ByteBuffer - getJNIMangledArg(java.nio.ByteBuffer.class, buf, true); + JavaType.appendJNIDescriptor(buf, java.nio.ByteBuffer.class, true); } else if (type.isArrayOfCompoundTypeWrappers()) { // Mangle arrays of C structs as ByteBuffer[] final java.nio.ByteBuffer[] tmp = new java.nio.ByteBuffer[0]; - getJNIMangledArg(tmp.getClass(), buf, true); + JavaType.appendJNIDescriptor(buf, tmp.getClass(), true); } else if (type.isJNIEnv()) { // These are not exposed at the Java level } else { @@ -1514,54 +1325,6 @@ public class CMethodBindingEmitter extends FunctionEmitter { return buf; } - public static void getJNIMangledArg(final Class<?> c, final StringBuilder res, final boolean syntheticArgument) { - if (c.isPrimitive()) { - if (c == Boolean.TYPE) res.append("Z"); - else if (c == Byte.TYPE) res.append("B"); - else if (c == Character.TYPE) res.append("C"); - else if (c == Short.TYPE) res.append("S"); - else if (c == Integer.TYPE) res.append("I"); - else if (c == Long.TYPE) res.append("J"); - else if (c == Float.TYPE) res.append("F"); - else if (c == Double.TYPE) res.append("D"); - else throw new RuntimeException("Illegal primitive type \"" + c.getName() + "\""); - } else { - // Arrays and NIO Buffers are always passed down as java.lang.Object. - // The only arrays that show up as true arrays in the signature - // are the synthetic byte offset arrays created when passing - // down arrays of direct Buffers. Compound type wrappers are - // passed down as ByteBuffers (no good reason, just to avoid - // accidental conflation) so we mangle them differently. - if (syntheticArgument) { - if (c.isArray()) { - res.append("_3"); - final Class<?> componentType = c.getComponentType(); - // Handle arrays of compound type wrappers differently for - // convenience of the Java-level glue code generation - getJNIMangledArg(componentType, res, - (componentType == java.nio.ByteBuffer.class)); - } else { - res.append("L"); - res.append(c.getName().replace('.', '_')); - res.append("_2"); - } - } else { - if (c.isArray()) { - res.append("_3"); - getJNIMangledArg(c.getComponentType(), res, false); - } else if (c == java.lang.String.class) { - res.append("L"); - res.append(c.getName().replace('.', '_')); - res.append("_2"); - } else { - res.append("L"); - res.append("java_lang_Object"); - res.append("_2"); - } - } - } - } - private void emitOutOfMemoryCheck(final String varName, final String errorMessage) { unit.emitln(" if ( NULL == " + varName + " ) {"); unit.emitln(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryError\"),"); diff --git a/src/java/com/jogamp/gluegen/FunctionEmitter.java b/src/java/com/jogamp/gluegen/FunctionEmitter.java index a089a41..5037fc4 100644 --- a/src/java/com/jogamp/gluegen/FunctionEmitter.java +++ b/src/java/com/jogamp/gluegen/FunctionEmitter.java @@ -114,6 +114,7 @@ public abstract class FunctionEmitter { * Emit the function to the {@link #getUnit()} */ public final void emit() { + emitAdditionalCode(); emitDocComment(); //output.println(" // Emitter: " + getClass().getName()); emitSignature(); @@ -139,6 +140,7 @@ public abstract class FunctionEmitter { */ public CommentEmitter getCommentEmitter() { return commentEmitter; } + protected void emitAdditionalCode() { } protected void emitDocComment() { if (commentEmitter != null) { @@ -154,32 +156,42 @@ public abstract class FunctionEmitter { } } - protected void emitSignature() { + protected final void emitSignature() { + unit.emit(appendSignature(new StringBuilder()).toString()); + } - unit.emit(getBaseIndentString()); // indent method + protected StringBuilder appendSignature(final StringBuilder buf) { + buf.append(getBaseIndentString()); // indent method - final int numEmitted = emitModifiers(); + final int numEmitted = appendModifiers(buf); if (numEmitted > 0) { - unit.emit(" "); + buf.append(" "); } - emitReturnType(); - unit.emit(" "); + appendReturnType(buf); + buf.append(" "); - emitName(); - unit.emit("("); + appendName(buf); + buf.append("("); - emitArguments(); - unit.emit(")"); + appendArguments(buf); + buf.append(")"); + return buf; } - protected int emitModifiers() { + protected final int emitModifiers() { + final StringBuilder buf = new StringBuilder(); + final int n = appendModifiers(buf); + unit.emit(buf.toString()); + return n; + } + protected int appendModifiers(final StringBuilder buf) { int numEmitted = 0; for (final Iterator<EmissionModifier> it = getModifiers(); it.hasNext(); ) { - unit.emit(it.next().toString()); + buf.append(it.next().toString()); ++numEmitted; if (it.hasNext()) { - unit.emit(" "); + buf.append(" "); } } return numEmitted; @@ -190,10 +202,23 @@ public abstract class FunctionEmitter { protected String getCommentStartString() { return "/* "; } protected String getCommentEndString() { return " */"; } - protected abstract void emitReturnType(); - protected abstract void emitName(); + protected final void emitReturnType() { + unit.emit(appendReturnType(new StringBuilder()).toString()); + } + protected abstract StringBuilder appendReturnType(StringBuilder buf); + protected final void emitName() { + unit.emit(appendName(new StringBuilder()).toString()); + } + protected abstract StringBuilder appendName(StringBuilder buf); + /** Returns the number of arguments emitted. */ + protected final int emitArguments() { + final StringBuilder buf = new StringBuilder(); + final int n = appendArguments(buf); + unit.emit(buf.toString()); + return n; + } /** Returns the number of arguments emitted. */ - protected abstract int emitArguments(); + protected abstract int appendArguments(StringBuilder buf); protected abstract void emitBody(); public static class EmissionModifier { diff --git a/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java b/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java new file mode 100644 index 0000000..96e1e77 --- /dev/null +++ b/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java @@ -0,0 +1,666 @@ +/** + * 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; + +import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; +import com.jogamp.gluegen.cgram.types.Type; + +public final class JavaCallbackEmitter { + final JavaConfiguration cfg; + final MethodBinding binding; + final String setFuncSignature; + final JavaCallbackInfo info; + final String capIfaceName; + final String lowIfaceName; + final String lockInstanceName; + final String dataMapInstanceName; + final String dataInstanceName; + final String DataClassName; + final String fqUsrParamClassName; + final JavaType cbFuncJavaReturnType; + final String jcbNextIDVarName; + + final String setFuncCBArgName; + final Type setFuncUserParamCType; + final JavaType setFuncUserParamJType; + final String setFuncUserParamTypeName; + final String setFuncUserParamArgName; + + final boolean customKeyClass; + final String KeyClassName; + final boolean useDataMap; + + public JavaCallbackEmitter(final JavaConfiguration cfg, final MethodBinding mb, final JavaCallbackInfo javaCallback, final String setFuncSignature) { + this.cfg = cfg; + this.binding = mb; + this.setFuncSignature = setFuncSignature; + this.info = javaCallback; + + capIfaceName = CodeGenUtils.capitalizeString( mb.getInterfaceName() ); + lowIfaceName = CodeGenUtils.decapitalizeString( mb.getInterfaceName() ); + lockInstanceName = lowIfaceName+"Lock"; + dataMapInstanceName = lowIfaceName+"DataMap"; + dataInstanceName = lowIfaceName+"Data"; + DataClassName = capIfaceName+"Data"; + fqUsrParamClassName = cfg.packageName()+"."+cfg.className()+"."+DataClassName; + cbFuncJavaReturnType = javaCallback.cbFuncBinding.getJavaReturnType(); + jcbNextIDVarName = "NEXT_"+capIfaceName+"_ID"; + + setFuncCBArgName = binding.getArgumentName(javaCallback.setFuncCBParamIdx); + setFuncUserParamCType = mb.getCArgumentType(javaCallback.setFuncUserParamIdx); + setFuncUserParamJType = mb.getJavaArgumentType(javaCallback.setFuncUserParamIdx); + setFuncUserParamTypeName = setFuncUserParamJType.getName(); + setFuncUserParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); + + if( null != javaCallback.setFuncKeyClassName ) { + customKeyClass = true;; + KeyClassName = javaCallback.setFuncKeyClassName; + useDataMap = true; + } else { + customKeyClass = false; + KeyClassName = capIfaceName+"Key"; + useDataMap = javaCallback.setFuncKeyIndices.size() > 0; + } + } + + public void emitJavaSetFuncPreCall(final CodeUnit unit) { + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emit (" final long nativeUserParam = "); + if( setFuncUserParamJType.isLong() ) { + unit.emitln(" "+setFuncUserParamArgName+";"); + } else if( setFuncUserParamJType.isCompoundTypeWrapper() ) { + unit.emitln(" null != "+setFuncUserParamArgName+" ? "+setFuncUserParamArgName+".getDirectBufferAddress() : 0;"); + } else { + unit.emitln(""+jcbNextIDVarName+"++;"); + unit.emitln(" if( 0 >= "+jcbNextIDVarName+" ) { "+jcbNextIDVarName+" = 1; }"); + } + unit.emitln(" if( null != "+setFuncCBArgName+" ) {"); + unit.emitln(" add"+capIfaceName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), info.setFuncKeyIndices, true).toString()+ + "new "+DataClassName+"("+setFuncCBArgName+", "+setFuncUserParamArgName+"));"); + unit.emitln(" }"); + unit.emitln(); + } + + public void emitJavaSetFuncPostCall(final CodeUnit unit) { + unit.emitln(" if( null == "+setFuncCBArgName+" ) {"); + unit.emitln(" // callback released (null func) -> release a previously mapped instance "); + if( useDataMap ) { + unit.emitln(" release"+capIfaceName+"( new "+KeyClassName+"( "+binding.getJavaCallSelectArguments(new StringBuilder(), info.setFuncKeyIndices, false).toString()+" ) );"); + } else { + unit.emitln(" release"+capIfaceName+"();"); + } + unit.emitln(" }"); + unit.emitln(" } // synchronized "); + } + + public void emitJavaAdditionalCode(final CodeUnit unit, final boolean isInterface) { + if( isInterface ) { + if( useDataMap ) { + if( !customKeyClass && !info.keyClassEmitted ) { + emitJavaKeyClass(unit); + unit.emitln(); + info.keyClassEmitted = true; + } + emitJavaBriefAPIDoc(unit, "Returns ", "set of ", "", "for "); + unit.emitln(" public Set<"+KeyClassName+"> get"+capIfaceName+"Keys();"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns ", "whether callback ", "if callback ", "is mapped for "); + unit.emitln(" public boolean is"+capIfaceName+"Mapped("+KeyClassName+" key);"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns "+info.cbFuncTypeName+" callback ", "mapped to ", "", "for "); + unit.emitln(" public "+info.cbFuncTypeName+" get"+capIfaceName+"("+KeyClassName+" key);"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns user-param ", "mapped to ", "", "for "); + unit.emitln(" public Object get"+capIfaceName+"UserParam("+KeyClassName+" key);"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Releases all callback data ", "mapped via ", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public int releaseAll"+capIfaceName+"();"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Releases callback data ", "mapped to ", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public void release"+capIfaceName+"("+KeyClassName+" key);"); + unit.emitln(); + } else { + emitJavaBriefAPIDoc(unit, "Returns ", "whether callback ", "if callback ", "is mapped for "); + unit.emitln(" public boolean is"+capIfaceName+"Mapped();"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns "+info.cbFuncTypeName+" callback ", "mapped to ", "", "for "); + unit.emitln(" public "+info.cbFuncTypeName+" get"+capIfaceName+"();"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns user-param ", "mapped to ", "", "for "); + unit.emitln(" public Object get"+capIfaceName+"UserParam();"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Releases callback data ", "", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public void release"+capIfaceName+"();"); + unit.emitln(); + } + } else { + if( useDataMap ) { + if( !customKeyClass && !info.keyClassEmitted ) { + emitJavaKeyClass(unit); + unit.emitln(); + info.keyClassEmitted = true; + } + emitJavaBriefAPIDoc(unit, "Returns ", "set of ", "", "for "); + unit.emitln(" public final Set<"+KeyClassName+"> get"+capIfaceName+"Keys() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" return "+dataMapInstanceName+".keySet();"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Returns ", "whether callback ", "if callback ", "is mapped for "); + unit.emitln(" public final boolean is"+capIfaceName+"Mapped("+KeyClassName+" key) {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" return null != "+dataMapInstanceName+".get(key);"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + + emitJavaBriefAPIDoc(unit, "Returns "+info.cbFuncTypeName+" callback ", "mapped to ", "", "for "); + unit.emitln(" public final "+info.cbFuncTypeName+" get"+capIfaceName+"("+KeyClassName+" key) {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" final "+DataClassName+" value = "+dataMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.func : null;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + + emitJavaBriefAPIDoc(unit, "Returns user-param ", "mapped to ", "", "for "); + unit.emitln(" public final Object get"+capIfaceName+"UserParam("+KeyClassName+" key) {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" final "+DataClassName+" value = "+dataMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.param : null;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Releases all callback data ", "mapped via ", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public final int releaseAll"+capIfaceName+"() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" final Set<"+KeyClassName+"> keySet = "+dataMapInstanceName+".keySet();"); + unit.emitln(" final "+KeyClassName+"[] keys = keySet.toArray(new "+KeyClassName+"[keySet.size()]);"); + unit.emitln(" for(int i=0; i<keys.length; ++i) {"); + unit.emitln(" final "+KeyClassName+" key = keys[i];"); + unit.emitln(" release"+capIfaceName+"(key);"); + unit.emitln(" }"); + unit.emitln(" return keys.length;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + emitJavaBriefAPIDoc(unit, "Releases callback data ", "mapped to ", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public final void release"+capIfaceName+"("+KeyClassName+" key) {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" /* final "+DataClassName+" value = */ "+dataMapInstanceName+".remove(key);"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + } else { + emitJavaBriefAPIDoc(unit, "Returns ", "whether callback ", "if callback ", "is mapped for "); + unit.emitln(" public final boolean is"+capIfaceName+"Mapped() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" return null != "+dataInstanceName+";"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + + emitJavaBriefAPIDoc(unit, "Returns "+info.cbFuncTypeName+" callback ", "mapped to ", "", "for "); + unit.emitln(" public final "+info.cbFuncTypeName+" get"+capIfaceName+"() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" final "+DataClassName+" value = "+dataInstanceName+";"); + unit.emitln(" return null != value ? value.func : null;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + + emitJavaBriefAPIDoc(unit, "Returns user-param ", "mapped to ", "", "for "); + unit.emitln(" public final Object get"+capIfaceName+"UserParam() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" final "+DataClassName+" value = "+dataInstanceName+";"); + unit.emitln(" return null != value ? value.param : null;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + + emitJavaBriefAPIDoc(unit, "Releases callback data ", "", "", "skipping toolkit API. Favor passing `null` callback ref to "); + unit.emitln(" public final void release"+capIfaceName+"() {"); + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + unit.emitln(" // final "+DataClassName+" value = "+dataInstanceName+";"); + unit.emitln(" "+dataInstanceName+" = null;"); + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + } + unit.emitln(" private final void add"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), info.setFuncKeyIndices, true).toString()+DataClassName+" value) {"); + if( useDataMap ) { + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), info.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" /* final "+DataClassName+" old = */ "+dataMapInstanceName+".put(key, value);"); + } else { + unit.emitln(" // final "+DataClassName+" old = "+dataInstanceName+";"); + unit.emitln(" "+dataInstanceName+" = value;"); + } + unit.emitln(" }"); + unit.emitln(); + if( !cfg.emittedJavaCallbackUserParamClasses.contains(fqUsrParamClassName) ) { + emitJavaDataClass(unit); + cfg.emittedJavaCallbackUserParamClasses.add(fqUsrParamClassName); + } + if( useDataMap ) { + unit.emitln(" private static final Map<"+KeyClassName+", "+DataClassName+"> "+dataMapInstanceName+" = new HashMap<"+KeyClassName+", "+DataClassName+">();"); + } else { + unit.emitln(" private static "+DataClassName+" "+dataInstanceName+" = null;"); + } + unit.emitln(" private static long "+jcbNextIDVarName+" = 1;"); + unit.emitln(" private static final Object "+lockInstanceName+" = new Object();"); + unit.emitln(); + emitJavaStaticCallback(unit); + } + } + + private final void emitJavaBriefAPIDoc(final CodeUnit unit, final String actionText, final String relationToKey, final String noKeyText, final String relationToFunc) { + unit.emit(" /** "+actionText); + if( info.setFuncKeyIndices.size() > 0 ) { + unit.emit(relationToKey); + unit.emit("Key { "+binding.getJavaSelectParameter(new StringBuilder(), info.setFuncKeyIndices, false).toString()+" } "); + } else { + unit.emit(noKeyText); + } + unit.emit(relationToFunc); + unit.emitln("<br> <code>"+setFuncSignature+"</code> */"); + } + + private final void emitJavaKeyClass(final CodeUnit unit) { + emitJavaBriefAPIDoc(unit, "", "", "", "for "); + unit.emitln(" public static class "+KeyClassName+" {"); + binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && info.setFuncKeyIndices.contains(idx) ) { + unit.emitln(" public final "+jType+" "+name+";"); + return true; + } else { + return false; + } + } ); + unit.emitln(" public "+KeyClassName+"("+binding.getJavaSelectParameter(new StringBuilder(), info.setFuncKeyIndices, false).toString()+") {"); + binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && info.setFuncKeyIndices.contains(idx) ) { + unit.emitln(" this."+name+" = "+name+";"); + return true; + } else { + return false; + } + } ); + unit.emitln(" }"); + unit.emitln(" @Override"); + unit.emitln(" public boolean equals(final Object o) {"); + unit.emitln(" if( this == o ) {"); + unit.emitln(" return true;"); + unit.emitln(" }"); + unit.emitln(" if( !(o instanceof "+KeyClassName+") ) {"); + unit.emitln(" return false;"); + unit.emitln(" }"); + { + final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && info.setFuncKeyIndices.contains(idx) ) { + if( 0 == consumedCount ) { + unit.emitln(" final "+KeyClassName+" o2 = ("+KeyClassName+")o;"); + unit.emit (" return "); + } else { + unit.emitln(" &&"); + unit.emit (" "); + } + if( jType.isPrimitive() || idx == info.setFuncUserParamIdx ) { + unit.emit(name+" == o2."+name); + } else { + unit.emit(name+".equals( o2."+name+" )"); + } + return true; + } else { + return false; + } + } ); + if( 0 == count ) { + unit.emit(" return true"); + } + unit.emitln(";"); + } + unit.emitln(" }"); + unit.emitln(" @Override"); + unit.emitln(" public int hashCode() {"); + { + final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && info.setFuncKeyIndices.contains(idx) ) { + if( 0 == consumedCount ) { + unit.emitln(" // 31 * x == (x << 5) - x"); + unit.emit (" int hash = "); + } else { + unit.emit (" hash = ((hash << 5) - hash) + "); + } + if( jType.isPrimitive() ) { + if( jType.isLong() ) { + unit.emitln("HashUtil.getAddrHash32_EqualDist( "+name+" );"); + } else { + unit.emitln(name+";"); + } + } else { + if( idx == info.setFuncUserParamIdx ) { + unit.emitln("System.identityHashCode( "+name+" );"); + } else { + unit.emitln(name+".hashCode();"); + } + } + return true; + } else { + return false; + } + } ); + if( 0 == count ) { + unit.emitln(" return 0;"); + } else { + unit.emitln(" return hash;"); + } + } + unit.emitln(" }"); + unit.emitln(" }"); + } + + private final void emitJavaDataClass(final CodeUnit unit) { + unit.emitln(" private static class "+DataClassName+" {"); + unit.emitln(" // userParamArgCType "+setFuncUserParamCType); + unit.emitln(" // userParamArgJType "+setFuncUserParamJType); + unit.emitln(" final "+info.cbFuncTypeName+" func;"); + unit.emitln(" final "+setFuncUserParamTypeName+" param;"); + unit.emitln(" "+DataClassName+"("+info.cbFuncTypeName+" func, "+setFuncUserParamTypeName+" param) {"); + unit.emitln(" this.func = func;"); + unit.emitln(" this.param = param;"); + unit.emitln(" }"); + unit.emitln(" }"); + } + + public final String getJavaStaticCallbackSignature() { + final StringBuilder buf = new StringBuilder(); + buf.append("("); + info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() ) { + if( idx == info.cbFuncUserParamIdx ) { + buf.append("J"); + } else { + buf.append(jType.getDescriptor()); + } + return true; + } else { + return false; + } + } ); + buf.append(")"); + buf.append(cbFuncJavaReturnType.getDescriptor()); + return buf.toString(); + } + + public final int appendJavaAdditionalJNIParameter(final StringBuilder buf) { + buf.append("Class<?> clazz, String callbackSignature, long nativeUserParam"); + return 3; + } + public final int appendJavaAdditionalJNIArguments(final StringBuilder buf) { + buf.append("this.getClass(), \"" + getJavaStaticCallbackSignature()+ "\", nativeUserParam"); + return 3; + } + private final void emitJavaStaticCallback(final CodeUnit unit) { + unit.emitln(" /** Static callback invocation, dispatching to "+info.cbSimpleClazzName+" for callback <br> <code>"+ + info.cbFuncType.toString(info.cbFuncTypeName, false, true)+"</code> */"); + unit.emit (" /* pp */ static "+cbFuncJavaReturnType.getName()+" invoke"+capIfaceName+"("); + final boolean[] mapNativePtrToCompound = { false }; + final JavaType[] origUserParamJType = { null }; + info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() ) { + if( 0 < consumedCount ) { unit.emit(", "); } + if( idx == info.cbFuncUserParamIdx ) { + unit.emit("long nativeUserParamPtr"); + if( jType.isCompoundTypeWrapper() ) { + mapNativePtrToCompound[0] = true; + origUserParamJType[0] = jType; + } + } else { + unit.emit(jType+" "+name); + } + return true; + } else { + return false; + } + } ); + unit.emitln(") {"); + if( mapNativePtrToCompound[0] ) { + unit.emitln(" final "+origUserParamJType[0]+" "+info.cbFuncUserParamName+" = "+origUserParamJType[0]+".derefPointer(nativeUserParamPtr);"); + } + if( useDataMap ) { + unit.emitln(" final "+DataClassName+" value;"); + } else { + unit.emitln(" final "+DataClassName+" value;"); + } + unit.emitln(" synchronized( "+lockInstanceName+" ) {"); + if( useDataMap ) { + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), info.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" value = "+dataMapInstanceName+".get(key);"); + } else { + unit.emitln(" value = "+dataInstanceName+";"); + } + unit.emitln(" }"); + unit.emitln(" if( null == value ) {"); + if( !cbFuncJavaReturnType.isVoid() ) { + unit.emitln(" return 0;"); + } else { + unit.emitln(" return;"); + } + unit.emitln(" }"); + if( !cbFuncJavaReturnType.isVoid() ) { + unit.emit(" return "); + } else { + unit.emit(" "); + } + unit.emit("value.func.callback("); + info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() ) { + if( 0 < consumedCount ) { unit.emit(", "); } + if( idx == info.cbFuncUserParamIdx && !mapNativePtrToCompound[0] ) { + unit.emit("value.param"); + } else { + unit.emit(name); + } + return true; + } else { + return false; + } + } ); + unit.emitln(");"); + unit.emitln(" }"); + unit.emitln(); + } + + // + // C JNI Code .. + // + + public int appendCAdditionalParameter(final StringBuilder buf) { + buf.append(", jclass clazz, jstring jcallbackSignature, jlong jnativeUserParam"); + return 3; + } + + public void emitCOptArgumentSuffix(final CodeUnit unit, final int argIdx) { + if( ( argIdx == info.setFuncCBParamIdx || argIdx == info.setFuncUserParamIdx ) ) { + unit.emit("_native"); + } + } + + public void appendCAdditionalJNIDescriptor(final StringBuilder buf) { + JavaType.appendJNIDescriptor(buf, Class.class, false); // to account for the additional 'jclass clazz' parameter + JavaType.appendJNIDescriptor(buf, String.class, false); // to account for the additional 'jstring jcallbackSignature' parameter + JavaType.appendJNIDescriptor(buf, long.class, false); // to account for the additional 'long nativeUserParam' parameter + } + + public void emitCSetFuncPreCall(final CodeUnit unit) { + final String jcbNativeBasename = CodeGenUtils.capitalizeString( info.setFuncName ); + final String jcbFriendlyBasename = info.setFuncName+"("+info.cbSimpleClazzName+")"; + final String staticBindingMethodName = "invoke"+jcbNativeBasename; + final String staticBindingClazzVarName = "clazz"+jcbNativeBasename; + final String staticBindingMethodIDVarName = "method"+jcbNativeBasename; + final String cbFuncArgName = binding.getArgumentName(info.setFuncCBParamIdx); + final String userParamTypeName = info.cbFuncUserParamType.getCName(); + final String userParamArgName = binding.getArgumentName(info.setFuncUserParamIdx); + final String nativeCBFuncVarName = cbFuncArgName+"_native"; + final String nativeUserParamVarName = userParamArgName+"_native"; + unit.emitln(); + unit.emitln(" // JavaCallback handling"); + unit.emitln(" if( NULL == clazz ) { (*env)->FatalError(env, \"NULL clazz passed to '"+jcbFriendlyBasename+"'\"); }"); + unit.emitln(" "+info.cbFuncTypeName+" "+nativeCBFuncVarName+";"); + unit.emitln(" "+userParamTypeName+"* "+nativeUserParamVarName+";"); + unit.emitln(" if( NULL != "+cbFuncArgName+" ) {"); + unit.emitln(" const char* callbackSignature = (*env)->GetStringUTFChars(env, jcallbackSignature, (jboolean*)NULL);"); + unit.emitln(" if( NULL == callbackSignature ) { (*env)->FatalError(env, \"Failed callbackSignature in '"+jcbFriendlyBasename+"'\"); }"); + unit.emitln(" jmethodID cbMethodID = (*env)->GetStaticMethodID(env, clazz, \""+staticBindingMethodName+"\", callbackSignature);"); + unit.emitln(" if( NULL == cbMethodID ) {"); + unit.emitln(" char cmsg[400];"); + unit.emitln(" snprintf(cmsg, 400, \"Failed GetStaticMethodID of '"+staticBindingMethodName+"(%s)' in '"+jcbFriendlyBasename+"'\", callbackSignature);"); + unit.emitln(" (*env)->FatalError(env, cmsg);"); + unit.emitln(" }"); + unit.emitln(" (*env)->ReleaseStringUTFChars(env, jcallbackSignature, callbackSignature);"); + unit.emitln(" "+staticBindingClazzVarName+" = clazz;"); + unit.emitln(" "+staticBindingMethodIDVarName+" = cbMethodID;"); + unit.emitln(" "+nativeCBFuncVarName+" = func"+jcbNativeBasename+";"); + unit.emitln(" "+nativeUserParamVarName+" = ("+userParamTypeName+"*) jnativeUserParam;"); + unit.emitln(" } else {"); + unit.emitln(" "+nativeCBFuncVarName+" = NULL;"); + unit.emitln(" "+nativeUserParamVarName+" = NULL;"); + unit.emitln(" }"); + unit.emitln(); + + } + + public void emitCAdditionalCode(final CodeUnit unit, final CMethodBindingEmitter jcbCMethodEmitter) { + final String jcbNativeBasename = CodeGenUtils.capitalizeString( info.setFuncName ); + final String jcbFriendlyBasename = info.setFuncName+"("+info.cbSimpleClazzName+")"; + final String staticBindingClazzVarName = "clazz"+jcbNativeBasename; + final String staticBindingMethodIDVarName = "method"+jcbNativeBasename; + final String staticCallbackName = "func"+jcbNativeBasename; + // final Type userParamType = javaCallback.cbFuncBinding.getCArgumentType(javaCallback.cbFuncUserParamIdx); + final String userParamTypeName = info.cbFuncUserParamType.getCName(); + final String userParamArgName = info.cbFuncBinding.getArgumentName(info.cbFuncUserParamIdx); + final Type cReturnType = info.cbFuncBinding.getCReturnType(); + final JavaType jretType = info.cbFuncBinding.getJavaReturnType(); + unit.emitln(); + unit.emitln("static jclass "+staticBindingClazzVarName+" = NULL;"); + unit.emitln("static jmethodID "+staticBindingMethodIDVarName+" = NULL;"); + unit.emitln(); + // javaCallback.cbFuncCEmitter.emitSignature(); + unit.emit("static "+cReturnType.getCName()+" "+staticCallbackName+"("); + // javaCallback.cbFuncCEmitter.emitArguments(); + unit.emit(info.cbFuncBinding.getCParameterList(new StringBuilder(), false, null).toString()); + unit.emitln(") {"); + // javaCallback.cbFuncCEmitter.emitBody(); + { + unit.emitln(" JNIEnv* env = JVMUtil_GetJNIEnv();"); + unit.emitln(" jclass cbClazz = "+staticBindingClazzVarName+";"); + unit.emitln(" jmethodID cbMethod = "+staticBindingMethodIDVarName+";"); + unit.emitln(" if( NULL == env || NULL == cbClazz || NULL == cbMethod ) {"); + if( !cReturnType.isVoid() ) { + unit.emitln(" return 0;"); + } else { + unit.emitln(" return;"); + } + unit.emitln(" }"); + // javaCallback.cbFuncCEmitter.emitBodyVariableDeclarations(); + // javaCallback.cbFuncCEmitter.emitBodyUserVariableDeclarations(); + // javaCallback.cbFuncCEmitter.emitBodyVariablePreCallSetup(); + emitJavaCallbackBodyCToJavaPreCall(jcbCMethodEmitter); + + // javaCallback.cbFuncCEmitter.emitBodyCallCFunction(); + unit.emitln(" "+userParamTypeName+"* "+userParamArgName+"_jni = ("+userParamTypeName+"*) "+userParamArgName+";"); + unit.emitln(" // C Params: "+info.cbFuncBinding.getCParameterList(new StringBuilder(), false, null).toString()); + unit.emitln(" // J Params: "+info.cbFuncBinding.getJavaParameterList(new StringBuilder()).toString()); + + final String returnStatement; + if( !cReturnType.isVoid() ) { + unit.emit(" "+cReturnType.getCName()+" _res = 0;"); + returnStatement = "return _res;"; + } else { + returnStatement = "return;"; + } + if( !cReturnType.isVoid() ) { + unit.emit(" _res = ("+cReturnType.getCName()+") "); + } else { + unit.emit(" "); + } + unit.emit("(*env)->CallStatic" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cbClazz, cbMethod, "); + // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); + emitJavaCallbackBodyPassJavaArguments(unit, jcbCMethodEmitter.binding, null); //"NULL"); + unit.emitln(");"); + unit.emitln(" if( (*env)->ExceptionCheck(env) ) {"); + unit.emitln(" fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': Exception in Java Callback caught:\\n\");"); + unit.emitln(" (*env)->ExceptionDescribe(env);"); + unit.emitln(" (*env)->ExceptionClear(env);"); + unit.emitln(" }"); + + // javaCallback.cbFuncCEmitter.emitBodyUserVariableAssignments(); + // javaCallback.cbFuncCEmitter.emitBodyVariablePostCallCleanup(); + // javaCallback.cbFuncCEmitter.emitBodyMapCToJNIType(-1 /* return value */, true /* addLocalVar */) + + unit.emitln(" "+returnStatement); + } + unit.emitln("}"); + unit.emitln(); + } + + /* pp */ int emitJavaCallbackBodyCToJavaPreCall(final CMethodBindingEmitter ce) { + int count = 0; + for (int i = 0; i < ce.binding.getNumArguments(); i++) { + if( i == info.cbFuncUserParamIdx ) { + continue; + } + if( ce.emitBodyMapCToJNIType(i, true /* addLocalVar */) ) { + ++count; + } + } + return count; + } + + /* pp */ int emitJavaCallbackBodyPassJavaArguments(final CodeUnit unit, final MethodBinding binding, final String userParamVarName) { + int count = 0; + boolean needsComma = false; + for (int i = 0; i < binding.getNumArguments(); i++) { + if (needsComma) { + unit.emit(", "); + needsComma = false; + } + if( i == info.cbFuncUserParamIdx && null != userParamVarName ) { + unit.emit( userParamVarName ); + } else { + unit.emit( binding.getArgumentName(i) + "_jni" ); + } + needsComma = true; + ++count; + } + return count; + } + + +} diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 3e2e680..10f43a7 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -156,17 +156,19 @@ public class JavaConfiguration { final int cbFuncUserParamIdx; final String setFuncName; final List<Integer> setFuncKeyIndices = new ArrayList<Integer>(); + final int setFuncUserParamIdx; // optional final String setFuncKeyClassName; // optional - JavaCallbackDef(final String cbFuncTypeName, final int cbFuncUserParamIdx, final String setFuncName, final String setFuncKeyClassName) { + JavaCallbackDef(final String cbFuncTypeName, final int cbFuncUserParamIdx, final String setFuncName, final int setFuncUserParamIdx, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; this.cbFuncUserParamIdx = cbFuncUserParamIdx; this.setFuncName = setFuncName; + this.setFuncUserParamIdx = setFuncUserParamIdx; this.setFuncKeyClassName = setFuncKeyClassName; } @Override public String toString() { - return String.format("JavaCallbackDef[cbFunc[type %s, userParamIdx %d], set[%s, keys %s, KeyClass %s]]", - cbFuncTypeName, cbFuncUserParamIdx, setFuncName, setFuncKeyIndices.toString(), setFuncKeyClassName); + return String.format("JavaCallbackDef[cbFunc[type %s, userParamIdx %d], set[%s, keys %s, userParamIdx %d, KeyClass %s]]", + cbFuncTypeName, cbFuncUserParamIdx, setFuncName, setFuncKeyIndices.toString(), setFuncUserParamIdx, setFuncKeyClassName); } } private final List<JavaCallbackDef> javaCallbackList = new ArrayList<JavaCallbackDef>(); @@ -1630,7 +1632,25 @@ public class JavaConfiguration { protected void readJavaCallbackDef(final StringTokenizer tok, final String filename, final int lineNo) { try { final String setFuncName = tok.nextToken(); - final String cbFuncTypeName = tok.nextToken(); + final int setFuncUserParamIdx; + final String cbFuncTypeName; + { + final String stok = tok.nextToken(); + int ival = -1; + String sval = null; + try { + ival = Integer.valueOf(stok); + } catch(final NumberFormatException nfe) { + sval = stok; + } + if( null == sval ) { + setFuncUserParamIdx = ival; + cbFuncTypeName = tok.nextToken(); + } else { + setFuncUserParamIdx = -1; + cbFuncTypeName = sval; + } + } final Integer cbFuncUserParamIdx = Integer.valueOf(tok.nextToken()); final String cbFuncKeyClassName; if( tok.hasMoreTokens() ) { @@ -1638,7 +1658,7 @@ public class JavaConfiguration { } else { cbFuncKeyClassName = null; } - final JavaCallbackDef jcd = new JavaCallbackDef(cbFuncTypeName, cbFuncUserParamIdx, setFuncName, cbFuncKeyClassName); + final JavaCallbackDef jcd = new JavaCallbackDef(cbFuncTypeName, cbFuncUserParamIdx, setFuncName, setFuncUserParamIdx, cbFuncKeyClassName); javaCallbackList.add(jcd); javaCallbackSetFuncToDef.put(setFuncName, jcd); } catch (final NoSuchElementException e) { @@ -2279,13 +2299,12 @@ public class JavaConfiguration { final String cbFuncTypeName; final String cbSimpleClazzName; final String cbFQClazzName; - final String cbMethodSignature; + final String staticCBMethodSignature; final FunctionType cbFuncType; final MethodBinding cbFuncBinding; final int cbFuncUserParamIdx; - - final Type userParamType; - final String userParamName; + final String cbFuncUserParamName; + final Type cbFuncUserParamType; final String setFuncName; final List<Integer> setFuncKeyIndices; @@ -2295,38 +2314,38 @@ public class JavaConfiguration { int setFuncUserParamIdx; boolean keyClassEmitted; - public JavaCallbackInfo(final String cbFuncTypeName, final String cbSimpleClazzName, final String cbFQClazzName, final String cbMethodSignature, + public JavaCallbackInfo(final String cbFuncTypeName, final String cbSimpleClazzName, final String cbFQClazzName, final String staticCBMethodSignature, final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int cbFuncUserParamIdx, - final String setFuncName, final List<Integer> setFuncKeyIndices, final String setFuncKeyClassName) { + final String setFuncName, final int setFuncUserParamIdx, final List<Integer> setFuncKeyIndices, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; this.cbSimpleClazzName = cbSimpleClazzName; this.cbFQClazzName = cbFQClazzName; - this.cbMethodSignature = cbMethodSignature; + this.staticCBMethodSignature = staticCBMethodSignature; this.cbFuncType = cbFuncType; this.cbFuncBinding = cbFuncBinding; - int paramIdx = -2; - Type paramType = null; - String paramName = null; - if( 0 <= cbFuncUserParamIdx && cbFuncUserParamIdx < cbFuncType.getNumArguments() ) { - final Type t = cbFuncType.getArgumentType(cbFuncUserParamIdx); - if( null != t && t.isPointer() ) { - // OK '<something>*' - paramIdx = cbFuncUserParamIdx; - paramName = cbFuncType.getArgumentName(cbFuncUserParamIdx); - paramType = t.getTargetType(); + { + int paramIdx = -2; + Type paramType = null; + String paramName = null; + if( 0 <= cbFuncUserParamIdx && cbFuncUserParamIdx < cbFuncType.getNumArguments() ) { + final Type t = cbFuncType.getArgumentType(cbFuncUserParamIdx); + if( null != t && t.isPointer() ) { + // OK '<something>*' + paramIdx = cbFuncUserParamIdx; + paramName = cbFuncType.getArgumentName(cbFuncUserParamIdx); + paramType = t.getTargetType(); + } } + this.cbFuncUserParamIdx = paramIdx; + this.cbFuncUserParamName = paramName; + this.cbFuncUserParamType = paramType; } - this.cbFuncUserParamIdx = paramIdx; - - this.userParamType = paramType; - this.userParamName = paramName; - this.setFuncName = setFuncName; this.setFuncKeyIndices = setFuncKeyIndices; this.setFuncKeyClassName = setFuncKeyClassName; this.setFuncProcessed = false; this.setFuncCBParamIdx = -1; - this.setFuncUserParamIdx = -1; + this.setFuncUserParamIdx = setFuncUserParamIdx; this.keyClassEmitted = false; } @@ -2335,7 +2354,13 @@ public class JavaConfiguration { if( 0 <= cbParamIdx && 0 <= userParamIdx ) { setFuncProcessed = true; setFuncCBParamIdx = cbParamIdx; - setFuncUserParamIdx = userParamIdx; + if( 0 <= setFuncUserParamIdx ) { + if( setFuncUserParamIdx != userParamIdx ) { + throw new IllegalArgumentException("Mismatch pre-set setFuncUserParamIdx "+setFuncUserParamIdx+", given "+userParamIdx+": "+toString()); + } + } else { + setFuncUserParamIdx = userParamIdx; + } } else { setFuncCBParamIdx = -1; setFuncUserParamIdx = -1; @@ -2346,8 +2371,8 @@ public class JavaConfiguration { @Override public String toString() { return String.format("JavaCallbackInfo[cbFunc[%s%s, userParam[idx %d, '%s', %s], set[%s(ok %b, cbIdx %d, upIdx %d, keys %s, KeyClass '%s'], %s]", - cbFuncTypeName, cbMethodSignature, - cbFuncUserParamIdx, userParamName, userParamType.getSignature(null).toString(), + cbFuncTypeName, staticCBMethodSignature, + cbFuncUserParamIdx, cbFuncUserParamName, cbFuncUserParamType.getSignature(null).toString(), setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, setFuncKeyIndices.toString(), setFuncKeyClassName, cbFuncType.toString(cbFuncTypeName, false, true)); diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index 2ea8d30..4a81a01 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -1471,9 +1471,9 @@ public class JavaEmitter implements GlueEmitter { throw new UnsupportedOperationException("Reused FuncTypeName "+jcbd.cbFuncTypeName+" used with different FuncUserParamIdx "+jcbi0.cbFuncUserParamIdx+" -> "+jcbd.cbFuncUserParamIdx+". Func "+ funcType.toString(jcbd.cbFuncTypeName, false, true)); } - final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, jcbi0.cbMethodSignature, + final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, jcbi0.staticCBMethodSignature, funcType, jcbi0.cbFuncBinding, jcbi0.cbFuncUserParamIdx, - jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + jcbd.setFuncName, jcbd.setFuncUserParamIdx, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); LOG.log(INFO, "JavaCallbackInfo: Reusing {0} -> {1}", jcbd.setFuncName, jcbi0); } else { @@ -1493,7 +1493,7 @@ public class JavaEmitter implements GlueEmitter { } final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, cbMethodSignature.toString(), funcType, cbFuncBinding, jcbd.cbFuncUserParamIdx, - jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + jcbd.setFuncName, jcbd.setFuncUserParamIdx, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); javaCallbackInterfaceMap.put(cbFQClazzName, jcbi1); LOG.log(INFO, "JavaCallbackInfo: Added {0} -> {1}", jcbd.setFuncName, jcbi1); @@ -3019,9 +3019,6 @@ public class JavaEmitter implements GlueEmitter { 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()); - if( cfg.getJavaCallbackList().size() > 0 ) { - cUnit().emitJavaCallbackGlueDataDecl(); - } } } catch (final Exception e) { throw new RuntimeException( @@ -3114,13 +3111,29 @@ public class JavaEmitter implements GlueEmitter { // Replace JavaCallback type with generated interface name jcbiSetFuncCBParamIdx=i; mappedType = JavaType.createForNamedClass( jcbi.cbFQClazzName ); - } else if( null != jcbi && jcbi.userParamName.equals( cArgName ) && + } else if( null != jcbi && i == jcbi.setFuncUserParamIdx && cArgType.isPointer() ) { + // Replace userParam argument '<userParamType>*' if 'void*' with Object + jcbiSetFuncUserParamIdx=i; + if( cArgType.getTargetType().isVoid() ) { + if( jcbi.cbFuncUserParamType.isCompound() ) { + mappedType = JavaType.createForClass(long.class); + } else { + mappedType = JavaType.forObjectClass(); + } + } + } else if( null != jcbi && jcbi.cbFuncUserParamName.equals( cArgName ) && ( !jcbi.setFuncProcessed || i == jcbi.setFuncUserParamIdx ) && - cArgType.isPointer() && jcbi.userParamType.equals( cArgType.getTargetType() ) ) + cArgType.isPointer() && jcbi.cbFuncUserParamType.equals( cArgType.getTargetType() ) ) { - // Replace optional userParam argument '<userParamType>*' with Object + // Replace userParam argument '<userParamType>*' if 'void*' with Object jcbiSetFuncUserParamIdx=i; - mappedType = JavaType.forObjectClass(); + if( cArgType.getTargetType().isVoid() ) { + if( jcbi.cbFuncUserParamType.isCompound() ) { + mappedType = JavaType.createForClass(long.class); + } else { + mappedType = JavaType.forObjectClass(); + } + } } else if (stringArgIndices != null && stringArgIndices.contains(i)) { // Take into account any ArgumentIsString configuration directives that apply // System.out.println("Forcing conversion of " + binding.getName() + " arg #" + i + " from byte[] to String "); @@ -3153,7 +3166,8 @@ public class JavaEmitter implements GlueEmitter { } 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); + LOG.log(INFO, "BindFunc.JavaCallback: {0}: set[cbParamIdx {1}, userParamIdx {2}], {3}, {4}", + sym.getName(), jcbiSetFuncCBParamIdx, jcbiSetFuncUserParamIdx, sym.getType().toString(sym.getName(), false, true), jcbi); } final MethodBinding mb = new MethodBinding(sym, delegationImplName, javaReturnType, javaArgumentTypes, diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 6a93973..fe4f82a 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -95,7 +95,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { private String returnedArrayLengthExpression; private boolean returnedArrayLengthExpressionOnlyForComments = false; - private final JavaCallbackInfo javaCallback; + private final JavaCallbackEmitter javaCallbackEmitter; // A suffix used to create a temporary outgoing array of Buffers to // represent an array of compound type wrappers @@ -134,7 +134,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else { setCommentEmitter(defaultInterfaceCommentEmitter); } - javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); + final JavaCallbackInfo javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); + if( null != javaCallback ) { + javaCallbackEmitter = new JavaCallbackEmitter(cfg, binding, javaCallback, appendSignature(new StringBuilder()).toString()); + } else { + javaCallbackEmitter = null; + } // !forImplementingMethodCall && !isInterface } @@ -156,7 +161,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { epilogue = arg.epilogue; returnedArrayLengthExpression = arg.returnedArrayLengthExpression; returnedArrayLengthExpressionOnlyForComments = arg.returnedArrayLengthExpressionOnlyForComments; - javaCallback = arg.javaCallback; + javaCallbackEmitter = arg.javaCallbackEmitter; } public boolean isNativeMethod() { return isNativeMethod; } @@ -262,8 +267,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } @Override - protected void emitReturnType() { - unit.emit(getReturnTypeString(false)); + protected StringBuilder appendReturnType(final StringBuilder buf) { + return buf.append(getReturnTypeString(false)); } protected String erasedTypeString(final JavaType type, final boolean skipBuffers) { @@ -335,33 +340,34 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } @Override - protected void emitName() { + protected StringBuilder appendName(final StringBuilder buf) { if (isPrivateNativeMethod) { - unit.emit(getNativeImplMethodName()); + buf.append(getNativeImplMethodName()); } else if( isInterface()) { - unit.emit(getInterfaceName()); + buf.append(getInterfaceName()); } else { - unit.emit(getImplName()); + buf.append(getImplName()); } + return buf; } @Override - protected int emitArguments() { + protected int appendArguments(final StringBuilder buf) { boolean needComma = false; int numEmitted = 0; if( hasModifier(JavaMethodBindingEmitter.NATIVE) && binding.isReturnCompoundByValue() ) { - unit.emit("final Class<?> _clazzBuffers"); + buf.append("final Class<?> _clazzBuffers"); ++numEmitted; needComma = true; } if (isPrivateNativeMethod && binding.hasContainingType()) { // Always emit outgoing "this" argument if (needComma) { - unit.emit(", "); + buf.append(", "); } - unit.emit("ByteBuffer "); - unit.emit(javaThisArgumentName()); + buf.append("ByteBuffer "); + buf.append(javaThisArgumentName()); ++numEmitted; needComma = true; } @@ -385,12 +391,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } if (needComma) { - unit.emit(", "); + buf.append(", "); } - unit.emit(erasedTypeString(type, false)); - unit.emit(" "); - unit.emit(getArgumentName(i)); + buf.append(erasedTypeString(type, false)); + buf.append(" "); + buf.append(getArgumentName(i)); ++numEmitted; needComma = true; @@ -398,12 +404,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { // Add Buffer and array index offset arguments after each associated argument if (forDirectBufferImplementation || forIndirectBufferAndArrayImplementation) { if (type.isNIOBuffer()) { - unit.emit(", int " + byteOffsetArgName(i)); + buf.append(", int " + byteOffsetArgName(i)); if(!useNIODirectOnly) { - unit.emit(", boolean " + isNIOArgName(i)); + buf.append(", boolean " + isNIOArgName(i)); } } else if (type.isNIOBufferArray()) { - unit.emit(", int[] " + byteOffsetArrayArgName(i)); + buf.append(", int[] " + byteOffsetArrayArgName(i)); } } @@ -412,17 +418,16 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { if(useNIOOnly) { throw new RuntimeException("NIO[Direct]Only "+binding+" is set, but "+getArgumentName(i)+" is a primitive array"); } - unit.emit(", int " + offsetArgName(i)); + buf.append(", int " + offsetArgName(i)); } } if( hasModifier(JavaMethodBindingEmitter.NATIVE) && - null != javaCallback ) + null != javaCallbackEmitter ) { if (needComma) { - unit.emit(", "); + buf.append(", "); } - unit.emit("String callbackSignature, Object lockObj, long[/*1*/] nativeUserParam"); - ++numEmitted; + numEmitted += javaCallbackEmitter.appendJavaAdditionalJNIParameter(buf); } return numEmitted; } @@ -455,125 +460,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { return getArgumentName(i) + "_offset"; } - private static final boolean DEBUG_JAVACALLBACK = false; - - private final void emitJavaCallbackBirefAPIDoc(final String actionText, final String relationToKey, final String noKeyText, final String relationToFunc) { - unit.emit(" /** "+actionText); - if( javaCallback.setFuncKeyIndices.size() > 0 ) { - unit.emit(relationToKey); - unit.emit("Key { "+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+" } "); - } else { - unit.emit(noKeyText); - } - unit.emit(relationToFunc); - unit.emit("<br> <code>"); - emitSignature(); - unit.emitln("</code> **/"); - } - private final void emitJavaCallbackKeyClass(final String capIfaceName, final String keyClassName) { - emitJavaCallbackBirefAPIDoc("", "", "", "for "); - unit.emitln(" public static class "+keyClassName+" {"); - binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { - unit.emitln(" public final "+jType+" "+name+";"); - return true; - } else { - return false; - } - } ); - unit.emitln(" public "+keyClassName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); - binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { - unit.emitln(" this."+name+" = "+name+";"); - return true; - } else { - return false; - } - } ); - unit.emitln(" }"); - unit.emitln(" @Override"); - unit.emitln(" public boolean equals(final Object o) {"); - unit.emitln(" if( this == o ) {"); - unit.emitln(" return true;"); - unit.emitln(" }"); - unit.emitln(" if( !(o instanceof "+keyClassName+") ) {"); - unit.emitln(" return false;"); - unit.emitln(" }"); - { - final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { - if( 0 == consumedCount ) { - unit.emitln(" final "+keyClassName+" o2 = ("+keyClassName+")o;"); - unit.emit (" return "); - } else { - unit.emitln(" &&"); - unit.emit (" "); - } - if( jType.isPrimitive() || idx == javaCallback.setFuncUserParamIdx ) { - unit.emit(name+" == o2."+name); - } else { - unit.emit(name+".equals( o2."+name+" )"); - } - return true; - } else { - return false; - } - } ); - if( 0 == count ) { - unit.emit(" return true"); - } - unit.emitln(";"); - } - unit.emitln(" }"); - unit.emitln(" @Override"); - unit.emitln(" public int hashCode() {"); - { - final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { - if( 0 == consumedCount ) { - unit.emitln(" // 31 * x == (x << 5) - x"); - unit.emit (" int hash = "); - } else { - unit.emit (" hash = ((hash << 5) - hash) + "); - } - if( jType.isPrimitive() ) { - if( jType.isLong() ) { - unit.emitln("HashUtil.getAddrHash32_EqualDist( "+name+" );"); - } else { - unit.emitln(name+";"); - } - } else { - if( idx == javaCallback.setFuncUserParamIdx ) { - unit.emitln("System.identityHashCode( "+name+" );"); - } else { - unit.emitln(name+".hashCode();"); - } - } - return true; - } else { - return false; - } - } ); - if( 0 == count ) { - unit.emitln(" return 0;"); - } else { - unit.emitln(" return hash;"); - } - } - unit.emitln(" }"); - unit.emitln(" }"); - } - private final void emitJavaCallbackDataClass(final String capIfaceName, final String dataClassName) { - unit.emitln(" private static class "+dataClassName+" {"); - unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); - unit.emitln(" final Object param;"); - unit.emitln(" final long nativeParam;"); - unit.emitln(" "+dataClassName+"("+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(" }"); + @Override + protected void emitAdditionalCode() { + if( null != javaCallbackEmitter && !isPrivateNativeMethod ) { + javaCallbackEmitter.emitJavaAdditionalCode(unit, isInterface()); + } } @Override @@ -594,201 +485,6 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } unit.emitln(" }"); } - if( null != javaCallback && !isPrivateNativeMethod ) { - final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); - final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); - final String lockInstanceName = lowIfaceName+"Lock"; - final String dataMapInstanceName = lowIfaceName+"DataMap"; - final String dataInstanceName = lowIfaceName+"Data"; - final boolean customKeyClass; - final String KeyClassName; - final boolean useDataMap; - if( null != javaCallback.setFuncKeyClassName ) { - customKeyClass = true;; - KeyClassName = javaCallback.setFuncKeyClassName; - useDataMap = true; - } else { - customKeyClass = false; - KeyClassName = CodeGenUtils.capitalizeString(capIfaceName+"Key"); - useDataMap = javaCallback.setFuncKeyIndices.size() > 0; - } - final String DataClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"Data" ); - final String fqUsrParamClassName = cfg.packageName()+"."+cfg.className()+"."+DataClassName; - unit.emitln(); - if( isInterface() ) { - if( useDataMap ) { - if( !customKeyClass && !javaCallback.keyClassEmitted ) { - emitJavaCallbackKeyClass(capIfaceName, KeyClassName); - unit.emitln(); - javaCallback.keyClassEmitted = true; - } - emitJavaCallbackBirefAPIDoc("Returns ", "set of ", "", "for "); - unit.emitln(" public Set<"+KeyClassName+"> get"+capIfaceName+"Keys();"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns ", "whether callback ", "if callback ", "is mapped for "); - unit.emitln(" public boolean is"+capIfaceName+"Mapped("+KeyClassName+" key);"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns "+javaCallback.cbFuncTypeName+" callback ", "mapped to ", "", "for "); - unit.emitln(" public "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+KeyClassName+" key);"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns user-param ", "mapped to ", "", "for "); - unit.emitln(" public Object get"+capIfaceName+"UserParam("+KeyClassName+" key);"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Releases all callback data ", "mapped via ", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public int releaseAll"+capIfaceName+"();"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Releases callback data ", "mapped to ", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public void release"+capIfaceName+"("+KeyClassName+" key);"); - unit.emitln(); - } else { - emitJavaCallbackBirefAPIDoc("Returns ", "whether callback ", "if callback ", "is mapped for "); - unit.emitln(" public boolean is"+capIfaceName+"Mapped();"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns "+javaCallback.cbFuncTypeName+" callback ", "mapped to ", "", "for "); - unit.emitln(" public "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"();"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns user-param ", "mapped to ", "", "for "); - unit.emitln(" public Object get"+capIfaceName+"UserParam();"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Releases callback data ", "", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public void release"+capIfaceName+"();"); - unit.emitln(); - } - } else { - if( useDataMap ) { - if( !customKeyClass && !javaCallback.keyClassEmitted ) { - emitJavaCallbackKeyClass(capIfaceName, KeyClassName); - unit.emitln(); - javaCallback.keyClassEmitted = true; - } - emitJavaCallbackBirefAPIDoc("Returns ", "set of ", "", "for "); - unit.emitln(" public final Set<"+KeyClassName+"> get"+capIfaceName+"Keys() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" return "+dataMapInstanceName+".keySet();"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Returns ", "whether callback ", "if callback ", "is mapped for "); - unit.emitln(" public final boolean is"+capIfaceName+"Mapped("+KeyClassName+" key) {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" return null != "+dataMapInstanceName+".get(key);"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - - emitJavaCallbackBirefAPIDoc("Returns "+javaCallback.cbFuncTypeName+" callback ", "mapped to ", "", "for "); - unit.emitln(" public final "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+KeyClassName+" key) {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataMapInstanceName+".get(key);"); - unit.emitln(" return null != value ? value.func : null;"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - - emitJavaCallbackBirefAPIDoc("Returns user-param ", "mapped to ", "", "for "); - unit.emitln(" public final Object get"+capIfaceName+"UserParam("+KeyClassName+" key) {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataMapInstanceName+".get(key);"); - unit.emitln(" return null != value ? value.param : null;"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Releases all callback data ", "mapped via ", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public final int releaseAll"+capIfaceName+"() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final Set<"+KeyClassName+"> keySet = "+dataMapInstanceName+".keySet();"); - unit.emitln(" final "+KeyClassName+"[] keys = keySet.toArray(new "+KeyClassName+"[keySet.size()]);"); - unit.emitln(" for(int i=0; i<keys.length; ++i) {"); - unit.emitln(" final "+KeyClassName+" key = keys[i];"); - unit.emitln(" release"+capIfaceName+"(key);"); - unit.emitln(" }"); - unit.emitln(" return keys.length;"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - emitJavaCallbackBirefAPIDoc("Releases callback data ", "mapped to ", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public final void release"+capIfaceName+"("+KeyClassName+" key) {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataMapInstanceName+".remove(key);"); - if( DEBUG_JAVACALLBACK ) { - unit.emitln(" System.err.println(\"ZZZ Release \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); - } - unit.emitln(" if( null != value ) {"); - unit.emitln(" release"+capIfaceName+"Impl(value.nativeParam);"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(" }"); - } else { - emitJavaCallbackBirefAPIDoc("Returns ", "whether callback ", "if callback ", "is mapped for "); - unit.emitln(" public final boolean is"+capIfaceName+"Mapped() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" return null != "+dataInstanceName+";"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - - emitJavaCallbackBirefAPIDoc("Returns "+javaCallback.cbFuncTypeName+" callback ", "mapped to ", "", "for "); - unit.emitln(" public final "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataInstanceName+";"); - unit.emitln(" return null != value ? value.func : null;"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - - emitJavaCallbackBirefAPIDoc("Returns user-param ", "mapped to ", "", "for "); - unit.emitln(" public final Object get"+capIfaceName+"UserParam() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataInstanceName+";"); - unit.emitln(" return null != value ? value.param : null;"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(); - - emitJavaCallbackBirefAPIDoc("Releases callback data ", "", "", "skipping toolkit API. Favor passing `null` callback ref to "); - unit.emitln(" public final void release"+capIfaceName+"() {"); - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final "+DataClassName+" value = "+dataInstanceName+";"); - unit.emitln(" "+dataInstanceName+" = null;"); - if( DEBUG_JAVACALLBACK ) { - unit.emitln(" System.err.println(\"ZZZ Release \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); - } - unit.emitln(" if( null != value ) {"); - unit.emitln(" release"+capIfaceName+"Impl(value.nativeParam);"); - unit.emitln(" }"); - unit.emitln(" }"); - unit.emitln(" }"); - } - unit.emitln(" private native void release"+capIfaceName+"Impl(long nativeUserParam);"); - unit.emitln(); - unit.emitln(" private final void add"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+DataClassName+" value) {"); - if( useDataMap ) { - unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); - unit.emitln(" final "+DataClassName+" old = "+dataMapInstanceName+".put(key, value);"); - } else { - unit.emitln(" final "+DataClassName+" old = "+dataInstanceName+";"); - unit.emitln(" "+dataInstanceName+" = value;"); - } - if( DEBUG_JAVACALLBACK ) { - unit.emitln(" System.err.println(\"ZZZ Map \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); - } - unit.emitln(" if( null != old ) {"); - unit.emitln(" release"+capIfaceName+"Impl(old.nativeParam);"); - unit.emitln(" }"); - unit.emitln(" }"); - if( !cfg.emittedJavaCallbackUserParamClasses.contains(fqUsrParamClassName) ) { - emitJavaCallbackDataClass(capIfaceName, DataClassName); - cfg.emittedJavaCallbackUserParamClasses.add(fqUsrParamClassName); - } - if( useDataMap ) { - unit.emitln(" private final Map<"+KeyClassName+", "+DataClassName+"> "+dataMapInstanceName+" = new HashMap<"+KeyClassName+", "+DataClassName+">();"); - } else { - unit.emitln(" private "+DataClassName+" "+dataInstanceName+" = null;"); - } - unit.emitln(" private final Object "+lockInstanceName+" = new Object();"); - unit.emitln(); - } - } } protected void emitPrologueOrEpilogue(final List<String> code) { @@ -901,15 +597,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { protected void emitReturnVariableSetupAndCall(final MethodBinding binding) { final JavaType returnType = binding.getJavaReturnType(); + boolean needsResultAssignment = false; - final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); - if( null != javaCallback ) { - final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); - final String lockInstanceName = lowIfaceName+"Lock"; - unit.emitln(" synchronized( "+lockInstanceName+" ) {"); - unit.emitln(" final long[] nativeUserParam = { 0 };"); - unit.emitln(); + if( null != javaCallbackEmitter ) { + javaCallbackEmitter.emitJavaSetFuncPreCall(unit); } if (!returnType.isVoid()) { unit.emit(" "); @@ -941,36 +633,9 @@ 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); - final String DataClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"Data" ); - final String KeyClassName; - final boolean useDataMap; - if( null != javaCallback.setFuncKeyClassName ) { - KeyClassName = javaCallback.setFuncKeyClassName; - useDataMap = true; - } else { - KeyClassName = CodeGenUtils.capitalizeString(capIfaceName+"Key"); - useDataMap = javaCallback.setFuncKeyIndices.size() > 0; - } - if( DEBUG_JAVACALLBACK ) { - unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam 0x\"+Long.toHexString(nativeUserParam[0]));"); - } + if( null != javaCallbackEmitter ) { unit.emitln(); - unit.emitln(" if( 0 != nativeUserParam[0] ) {"); - unit.emitln(" // callback registrated -> add will release a previously mapped instance "); - unit.emitln(" add"+capIfaceName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+ - "new "+DataClassName+"("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); - unit.emitln(" } else {"); - unit.emitln(" // callback released (null func) -> release a previously mapped instance "); - if( useDataMap ) { - unit.emitln(" release"+capIfaceName+"( new "+KeyClassName+"( "+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+" ) );"); - } else { - unit.emitln(" release"+capIfaceName+"();"); - } - unit.emitln(" }"); - unit.emitln(" } // synchronized "); + javaCallbackEmitter.emitJavaSetFuncPostCall(unit); } emitPostCallCleanup(binding); @@ -1094,14 +759,13 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { needComma = true; ++numArgsEmitted; } - if( null != javaCallback ) { - final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); - final String lockInstanceName = lowIfaceName+"Lock"; + if( null != javaCallbackEmitter ) { if (needComma) { unit.emit(", "); } - unit.emit("\"" + javaCallback.cbMethodSignature + "\", "+lockInstanceName+", nativeUserParam"); - ++numArgsEmitted; + final StringBuilder buf = new StringBuilder(); + numArgsEmitted += javaCallbackEmitter.appendJavaAdditionalJNIArguments(buf); + unit.emit(buf.toString()); } return numArgsEmitted; } @@ -1301,12 +965,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } } - protected class InterfaceCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { - - @Override - protected void emitBeginning(final FunctionEmitter emitter, final PrintWriter writer) { - writer.print("Interface to C language function: <br> "); - } + protected class InterfaceCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { + @Override + protected void emitBeginning(final FunctionEmitter emitter, final PrintWriter writer) { + writer.print("Interface to C language function: <br> "); } + } } diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java index 682a95a..3d6d839 100644 --- a/src/java/com/jogamp/gluegen/JavaType.java +++ b/src/java/com/jogamp/gluegen/JavaType.java @@ -334,6 +334,84 @@ public class JavaType { } /** + * Appends the descriptor (internal type signature) corresponding to the given Class<?> c. + * @param buf the StringBuilder sink + * @param c the Class<?> to append the descriptor for + * @param useTrueType if true, use the actual Class<?> name for non primitives, otherwise java.lang.Object will be used (flattened) + * @return the given StringBuilder sink for chaining + */ + public static StringBuilder appendDescriptor(final StringBuilder buf, final Class<?> c, final boolean useTrueType) { + if (c.isPrimitive()) { + if (c == Boolean.TYPE) buf.append("Z"); + else if (c == Byte.TYPE) buf.append("B"); + else if (c == Character.TYPE) buf.append("C"); + else if (c == Short.TYPE) buf.append("S"); + else if (c == Integer.TYPE) buf.append("I"); + else if (c == Long.TYPE) buf.append("J"); + else if (c == Float.TYPE) buf.append("F"); + else if (c == Double.TYPE) buf.append("D"); + else throw new RuntimeException("Illegal primitive type \"" + c.getName() + "\""); + } else { + // Arrays and NIO Buffers are always passed down as java.lang.Object. + // The only arrays that show up as true arrays in the signature + // are the synthetic byte offset arrays created when passing + // down arrays of direct Buffers. Compound type wrappers are + // passed down as ByteBuffers (no good reason, just to avoid + // accidental conflation) so we mangle them differently. + if (useTrueType) { + if (c.isArray()) { + buf.append("["); + final Class<?> componentType = c.getComponentType(); + // Handle arrays of compound type wrappers differently for + // convenience of the Java-level glue code generation + appendDescriptor(buf, componentType, + (componentType == java.nio.ByteBuffer.class)); + } else { + buf.append("L"); + buf.append(c.getName().replace('.', '/')); + buf.append(";"); + } + } else { + if (c.isArray()) { + buf.append("["); + appendDescriptor(buf, c.getComponentType(), false); + } else if (c == java.lang.String.class) { + buf.append("L"); + buf.append(c.getName().replace('.', '/')); + buf.append(";"); + } else { + buf.append("L"); + buf.append("java_lang_Object"); + buf.append(";"); + } + } + } + return buf; + } + + /** + * Appends the native (JNI) method-name descriptor corresponding to the given Class<?> c, + * i.e. replacing chars {@link #appendDescriptor(StringBuilder, Class, boolean)} as follows + * <ul> + * <li>`_` -> `_1`</li> + * <li>`/` -> `_`</li> + * <li>`;` -> `_2`</li> + * <li>`[` -> `_3`</li> + * </ul> + * Only the newly appended segment to the StringBuilder sink will be converted to (JNI) method-name using {@link #toJNIMethodDescriptor(StringBuilder, int)}. + * @param buf the StringBuilder sink + * @param c the Class<?> to append the descriptor for + * @param useTrueType if true, use the actual Class<?> name for non primitives, otherwise java.lang.Object will be used (flattened) + * @return the given StringBuilder sink for chaining + * @see JNI Spec 2, Chapter 2, Resolving Native Method Names + * @see #toJNIMethodDescriptor(StringBuilder) + */ + public static StringBuilder appendJNIDescriptor(final StringBuilder res, final Class<?> c, final boolean useTrueType) { + final int start = res.length(); + return toJNIMethodDescriptor( appendDescriptor(res, c, useTrueType), start ); + } + + /** * Converts the assumed descriptor (internal type signature) to a native (JNI) method-name descriptor, * i.e. replacing chars {@link #getDescriptor()} as follows * <ul> @@ -342,6 +420,7 @@ public class JavaType { * <li>`;` -> `_2`</li> * <li>`[` -> `_3`</li> * </ul> + * @param descriptor the char sequence holding the original descriptor * @see JNI Spec 2, Chapter 2, Resolving Native Method Names */ public static String toJNIMethodDescriptor(final String descriptor) { @@ -351,6 +430,36 @@ public class JavaType { .replace("[", "_3"); } + /** + * Converts the assumed descriptor (internal type signature) to a native (JNI) method-name descriptor, + * i.e. replacing chars {@link #getDescriptor()} as follows + * <ul> + * <li>`_` -> `_1`</li> + * <li>`/` -> `_`</li> + * <li>`;` -> `_2`</li> + * <li>`[` -> `_3`</li> + * </ul> + * @param descriptor the char buffer holding the original descriptor + * @param start start position of the segment to convert, use 0 if whole buffr shall be converted + * @return returns passed descriptor buffer for chaining + * @see JNI Spec 2, Chapter 2, Resolving Native Method Names + */ + public static StringBuilder toJNIMethodDescriptor(final StringBuilder descriptor, final int start) { + replace(descriptor, start, "_", "_1"); + replace(descriptor, start, "/", "_"); + replace(descriptor, start, ";", "_2"); + replace(descriptor, start, "[", "_3"); + return descriptor; + } + private static StringBuilder replace(final StringBuilder buf, int start, final String target, final String replacement) { + start = buf.indexOf(target, start); + while( 0 <= start ) { + buf.replace(start, start + target.length(), replacement); + start = buf.indexOf(target, start + replacement.length()); + } + return buf; + } + /** Returns the String corresponding to the JNI type for this type, or NULL if it can't be represented (i.e., it's a boxing class that we need to call getBuffer() on.) */ diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java index a280d6e..c20b07a 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java @@ -102,17 +102,16 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { } @Override - protected int emitArguments() { - int numEmitted = super.emitArguments(); + protected int appendArguments(final StringBuilder buf) { + int numEmitted = super.appendArguments(buf); if (callThroughProcAddress) { if (numEmitted > 0) { - unit.emit(", "); + buf.append(", "); } - unit.emit(procAddressJavaTypeName); - unit.emit(" procAddress"); + buf.append(procAddressJavaTypeName); + buf.append(" procAddress"); ++numEmitted; } - return numEmitted; } @@ -224,7 +223,7 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { protected String jniMangle(final MethodBinding binding) { final StringBuilder buf = new StringBuilder(super.jniMangle(binding)); if (callThroughProcAddress && 0 <= buf.indexOf("__") ) { - getJNIMangledArg(Long.TYPE, buf, false); // to account for the additional _addr_ parameter + JavaType.appendJNIDescriptor(buf, Long.TYPE, false); // to account for the additional _addr_ parameter } return buf.toString(); } diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java index 0d5de1c..b6ed21b 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java @@ -91,19 +91,18 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte } @Override - protected int emitArguments() { - int numEmitted = super.emitArguments(); + protected int appendArguments(final StringBuilder buf) { + int numEmitted = super.appendArguments(buf); if (callThroughProcAddress) { if (changeNameAndArguments) { if (numEmitted > 0) { - unit.emit(", "); + buf.append(", "); } - unit.emit("long procAddress"); + buf.append("long procAddress"); ++numEmitted; } } - return numEmitted; } |