diff options
author | Sven Gothel <[email protected]> | 2023-06-30 11:36:33 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-06-30 11:36:33 +0200 |
commit | a73c992290930e617c78241bae9fe20cb18a01a9 (patch) | |
tree | 85115a5cf25d8b5018a8c2d78244272f504851e0 /src/java | |
parent | b35d62425311ec50e6c80b07372bc411aa287bb4 (diff) |
GlueGen JavaCallback: Resolve key mapping of callback and associated resources via 'JavaCallbackKey' config and custom `SetCallback-KeyClass`
Updated unit test and doc accordingly.
Unit tests handle OpenAL's AL_SOFT_callback_buffer and AL_SOFT_events.
Tested global scope (no key, default) and 1 key (default) and 1 key (custom class).
Added more query functions, which all only take the `SetCallbackFunction` key arguments as specified.
Cleaned up JavaCallback* config class field naminig scheme.
Added 'synchronized (..Map) { }' block in crucial `SetCallbackFunction`,
rendering implementation thread safe.
Diffstat (limited to 'src/java')
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 32 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaConfiguration.java | 98 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 62 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java | 221 |
4 files changed, 314 insertions, 99 deletions
diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index 3975315..43d11b7 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -154,7 +154,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); if( null != javaCallback ) { - jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.simpleCbClazzName.replace("_", "") ); + jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.cbSimpleClazzName.replace("_", "") ); jcbCMethodEmitter = new CMethodBindingEmitter(javaCallback.cbFuncBinding, unit, javaPackageName, javaClassName, isOverloadedBinding, isJavaMethodStatic, forImplementingMethodCall, @@ -324,12 +324,13 @@ 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 userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.userParamIdx); + final String userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.cbFuncUserParamIdx); final Type cReturnType = javaCallback.cbFuncBinding.getCReturnType(); final JavaType jretType = javaCallback.cbFuncBinding.getJavaReturnType(); unit.emitln("typedef struct {"); @@ -362,7 +363,6 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(" T_"+jcbNativeBasename+"* cb = (T_"+jcbNativeBasename+"*) "+userParamArgName+";"); unit.emitln(" // C Params: "+javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), false, null).toString()); unit.emitln(" // J Params: "+javaCallback.cbFuncBinding.getJavaParameterList(new StringBuilder()).toString()); - // unit.emitln(" fprintf(stderr, \"YYY Callback01 user %p, id %ld, msg %s\\n\", cb, id, msg);"); if( !cReturnType.isVoid() ) { unit.emit(" "+cReturnType.getCName()+" _res = ("+cReturnType.getCName()+") "); @@ -371,10 +371,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { } unit.emit("(*env)->Call" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cb->cbFunc, cb->cbMethodID, "); // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); - if( 0 < jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback) ) { - unit.emit(", "); - } - unit.emitln("cb->userParam);"); + jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback, "cb->userParam"); + unit.emitln(");"); // javaCallback.cbFuncCEmitter.emitBodyUserVariableAssignments(); // javaCallback.cbFuncCEmitter.emitBodyVariablePostCallCleanup(); @@ -393,7 +391,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { /* pp */ int emitJavaCallbackBodyCToJavaPreCall(final JavaCallbackInfo jcbi) { int count = 0; for (int i = 0; i < binding.getNumArguments(); i++) { - if( i == jcbi.userParamIdx ) { + if( i == jcbi.cbFuncUserParamIdx ) { continue; } if( emitBodyMapCToJNIType(i, true /* addLocalVar */) ) { @@ -402,18 +400,19 @@ public class CMethodBindingEmitter extends FunctionEmitter { } return count; } - /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi) { + /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi, final String userParamVarName) { int count = 0; boolean needsComma = false; for (int i = 0; i < binding.getNumArguments(); i++) { - if( i == jcbi.userParamIdx ) { - continue; - } if (needsComma) { unit.emit(", "); needsComma = false; } - unit.emit( binding.getArgumentName(i) + "_jni" ); + if( i == jcbi.cbFuncUserParamIdx ) { + unit.emit( userParamVarName ); + } else { + unit.emit( binding.getArgumentName(i) + "_jni" ); + } needsComma = true; ++count; } @@ -552,7 +551,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(" {"); unit.emitln(" jlong v = (jlong) (intptr_t) "+nativeUserParamVarName+";"); unit.emitln(" (*env)->SetLongArrayRegion(env, jnativeUserParam, 0, (jsize)1, &v);"); - // unit.emitln(" fprintf(stderr, \"YYY MessageCallback01 user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" fprintf(stderr, \"YYY user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); + } unit.emitln(" }"); unit.emitln(); } @@ -565,9 +566,10 @@ 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"+getInterfaceName()+"Impl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); + unit.emitln("_release"+capIfaceName+"MapImpl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); unit.emitln(" T_"+jcbNativeBasename+"* nativeUserParam = (T_"+jcbNativeBasename+"*) (intptr_t) jnativeUserParam;"); unit.emitln(" if( NULL != nativeUserParam ) {"); unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->cbFunc);"); diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 17ac547..1fe2747 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -152,18 +152,21 @@ public class JavaConfiguration { /** JavaCallback configuration definition (static) */ public static class JavaCallbackDef { - final String setFuncName; final String cbFuncTypeName; - final int userParamIdx; - JavaCallbackDef(final String setFuncName, final String cbFuncTypeName, final int userParamIdx) { - this.setFuncName = setFuncName; + final int cbFuncUserParamIdx; + final String setFuncName; + final List<Integer> setFuncKeyIndices = new ArrayList<Integer>(); + final String setFuncKeyClassName; // optional + JavaCallbackDef(final String cbFuncTypeName, final int cbFuncUserParamIdx, final String setFuncName, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; - this.userParamIdx = userParamIdx; + this.cbFuncUserParamIdx = cbFuncUserParamIdx; + this.setFuncName = setFuncName; + this.setFuncKeyClassName = setFuncKeyClassName; } @Override public String toString() { - return String.format("JavaCallbackDef[set %s, cb %s, userParamIdx %d]", - setFuncName, cbFuncTypeName, userParamIdx); + return String.format("JavaCallbackDef[cbFunc[type %s, userParamIdx %d], set[%s, keys %s, KeyClass %s]]", + cbFuncTypeName, cbFuncUserParamIdx, setFuncName, setFuncKeyIndices.toString(), setFuncKeyClassName); } } private final List<JavaCallbackDef> javaCallbackList = new ArrayList<JavaCallbackDef>(); @@ -1377,6 +1380,8 @@ public class JavaConfiguration { readArgumentIsString(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("JavaCallbackDef")) { readJavaCallbackDef(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("JavaCallbackKey")) { + readJavaCallbackKey(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsIgnore")) { readExtendedIntfImplSymbols(tok, filename, lineNo, true, false, false); } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsOnly")) { @@ -1626,8 +1631,14 @@ public class JavaConfiguration { try { final String setFuncName = tok.nextToken(); final String cbFuncTypeName = tok.nextToken(); - final Integer userParamIdx = Integer.valueOf(tok.nextToken()); - final JavaCallbackDef jcd = new JavaCallbackDef(setFuncName, cbFuncTypeName, userParamIdx); + final Integer cbFuncUserParamIdx = Integer.valueOf(tok.nextToken()); + final String cbFuncKeyClassName; + if( tok.hasMoreTokens() ) { + cbFuncKeyClassName = tok.nextToken(); + } else { + cbFuncKeyClassName = null; + } + final JavaCallbackDef jcd = new JavaCallbackDef(cbFuncTypeName, cbFuncUserParamIdx, setFuncName, cbFuncKeyClassName); javaCallbackList.add(jcd); javaCallbackSetFuncToDef.put(setFuncName, jcd); } catch (final NoSuchElementException e) { @@ -1636,6 +1647,26 @@ public class JavaConfiguration { } } + protected void readJavaCallbackKey(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final String setFuncName = tok.nextToken(); + final JavaCallbackDef jcd = javaCallbackSetFuncToDef.get(setFuncName); + if( null == jcd ) { + throw new IllegalArgumentException("JavaCallbackDef '"+setFuncName+"\' not (yet) defined."); + } + while( tok.hasMoreTokens() ) { + final int idx = Integer.valueOf(tok.nextToken()); + if( 0 > idx ) { + throw new IllegalArgumentException("JavaCallbackKey '"+setFuncName+"\' index "+idx+" not in range [0..n]."); + } + jcd.setFuncKeyIndices.add( idx ); + } + } catch (final NoSuchElementException e) { + throw new RuntimeException("Error parsing \"JavaCallbackKey\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readExtendedIntfImplSymbols(final StringTokenizer tok, final String filename, final int lineNo, final boolean forInterface, final boolean forImplementation, final boolean onlyList) { File javaFile; BufferedReader javaReader; @@ -2245,44 +2276,53 @@ public class JavaConfiguration { * @see JavaConfiguration#setFuncToJavaCallbackMap */ public static class JavaCallbackInfo { - final String setFuncName; final String cbFuncTypeName; - final String simpleCbClazzName; - final String fqCbClazzName; + final String cbSimpleClazzName; + final String cbFQClazzName; final String cbMethodSignature; final FunctionType cbFuncType; final MethodBinding cbFuncBinding; - final int userParamIdx; + final int cbFuncUserParamIdx; + final Type userParamType; final String userParamName; + + final String setFuncName; + final List<Integer> setFuncKeyIndices; + final String setFuncKeyClassName; boolean setFuncProcessed; int setFuncCBParamIdx; int setFuncUserParamIdx; - public JavaCallbackInfo(final String setFuncName, final String cbFuncTypeName, final String simpleClazzName, final String fqClazzName, final String methodSignature, - final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int userParamIdx) { - this.setFuncName = setFuncName; + public JavaCallbackInfo(final String cbFuncTypeName, final String cbSimpleClazzName, final String cbFQClazzName, final String cbMethodSignature, + final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int cbFuncUserParamIdx, + final String setFuncName, final List<Integer> setFuncKeyIndices, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; - this.simpleCbClazzName = simpleClazzName; - this.fqCbClazzName = fqClazzName; - this.cbMethodSignature = methodSignature; + this.cbSimpleClazzName = cbSimpleClazzName; + this.cbFQClazzName = cbFQClazzName; + this.cbMethodSignature = cbMethodSignature; this.cbFuncType = cbFuncType; this.cbFuncBinding = cbFuncBinding; int paramIdx = -2; Type paramType = null; String paramName = null; - if( 0 <= userParamIdx && userParamIdx < cbFuncType.getNumArguments() ) { - final Type t = cbFuncType.getArgumentType(userParamIdx); + if( 0 <= cbFuncUserParamIdx && cbFuncUserParamIdx < cbFuncType.getNumArguments() ) { + final Type t = cbFuncType.getArgumentType(cbFuncUserParamIdx); if( null != t && t.isPointer() ) { // OK '<something>*' - paramIdx = userParamIdx; - paramName = cbFuncType.getArgumentName(userParamIdx); + paramIdx = cbFuncUserParamIdx; + paramName = cbFuncType.getArgumentName(cbFuncUserParamIdx); paramType = t.getTargetType(); } } - this.userParamIdx = paramIdx; + 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; @@ -2303,12 +2343,16 @@ public class JavaConfiguration { @Override public String toString() { - return String.format("JavaCallbackInfo[set %s(ok %b, cbIdx %d, upIdx %d), cb %s%s, userParam[idx %d, '%s', %s], %s]", - setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, + 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, - userParamIdx, userParamName, userParamType.getSignature(null).toString(), cbFuncType.toString(cbFuncTypeName, false, true)); + cbFuncUserParamIdx, userParamName, userParamType.getSignature(null).toString(), + setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, + setFuncKeyIndices.toString(), setFuncKeyClassName, + cbFuncType.toString(cbFuncTypeName, false, true)); } } /** Mapped binding name to {@link JavaCallbackInfo} */ /* pp */ final Map<String, JavaCallbackInfo> setFuncToJavaCallbackMap = new HashMap<String, JavaCallbackInfo>(); + final Set<String> emittedJavaCallbackUserParamClasses = new HashSet<String>(); + } diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index 7dad237..7674e1f 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -75,7 +75,6 @@ import com.jogamp.common.os.DynamicLookupHelper; import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.util.ArrayHashMap; import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; -import com.jogamp.gluegen.FunctionEmitter.EmissionModifier; import com.jogamp.gluegen.JavaConfiguration.JavaCallbackDef; import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; import com.jogamp.gluegen.Logging.LoggerIf; @@ -1461,27 +1460,46 @@ public class JavaEmitter implements GlueEmitter { funcSym.addAliasedName(jcbd.cbFuncTypeName); LOG.log(INFO, "JavaCallback: fSym {0}, {1}", funcSym.getAliasedString(), jcbd); - final String simpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName); - final String fqClazzName = cfg.packageName()+"."+cfg.className()+"."+simpleClazzName; - final StringBuilder methodSignature = new StringBuilder(); - javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */"); - javaUnit.emitln(" public static interface "+simpleClazzName+" {"); - final List<MethodBinding> mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd.userParamIdx, methodSignature); - javaUnit.emitln(" }"); - javaUnit.emitln(); - if( 1 != mbs.size() ) { - throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); - } - final MethodBinding mb = mbs.get(0); - if( !mb.getJavaReturnType().isVoid() && !mb.getJavaReturnType().isPrimitive() ) { - throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+ - mb.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); - } - final JavaCallbackInfo jcbi = new JavaCallbackInfo(jcbd.setFuncName, jcbd.cbFuncTypeName, simpleClazzName, fqClazzName, - methodSignature.toString(), funcType, mb, jcbd.userParamIdx); - cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi); - LOG.log(INFO, "JavaCallbackInfo: Added {0}", jcbi); + final String cbSimpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName); + final String cbFQClazzName = cfg.packageName()+"."+cfg.className()+"."+cbSimpleClazzName; + + final JavaCallbackInfo jcbi0 = javaCallbackInterfaceMap.get(cbFQClazzName); + if( null != jcbi0 ) { + // Reuse callback-func interface, can't duplicate + if( jcbi0.cbFuncUserParamIdx != jcbd.cbFuncUserParamIdx ) { + 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, + funcType, jcbi0.cbFuncBinding, jcbi0.cbFuncUserParamIdx, + jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); + LOG.log(INFO, "JavaCallbackInfo: Reusing {0} -> {1}", jcbd.setFuncName, jcbi0); + } else { + final StringBuilder cbMethodSignature = new StringBuilder(); + javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */"); + javaUnit.emitln(" public static interface "+cbSimpleClazzName+" {"); + final List<MethodBinding> mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd.cbFuncUserParamIdx, cbMethodSignature); + javaUnit.emitln(" }"); + javaUnit.emitln(); + if( 1 != mbs.size() ) { + throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final MethodBinding cbFuncBinding = mbs.get(0); + if( !cbFuncBinding.getJavaReturnType().isVoid() && !cbFuncBinding.getJavaReturnType().isPrimitive() ) { + throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+ + cbFuncBinding.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, cbMethodSignature.toString(), + funcType, cbFuncBinding, jcbd.cbFuncUserParamIdx, + jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); + javaCallbackInterfaceMap.put(cbFQClazzName, jcbi1); + LOG.log(INFO, "JavaCallbackInfo: Added {0} -> {1}", jcbd.setFuncName, jcbi1); + } } + private final Map<String, JavaCallbackInfo> javaCallbackInterfaceMap = new HashMap<String, JavaCallbackInfo>(); + private List<MethodBinding> generateFunctionInterfaceCode(final JavaCodeUnit javaUnit, final FunctionSymbol funcSym, final int userParamIdx, final StringBuilder methodSignature) { // Emit method call and associated native code MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, null, null); @@ -3085,7 +3103,7 @@ public class JavaEmitter implements GlueEmitter { { // Replace JavaCallback type with generated interface name jcbiSetFuncCBParamIdx=i; - mappedType = JavaType.createForNamedClass( jcbi.fqCbClazzName ); + mappedType = JavaType.createForNamedClass( jcbi.cbFQClazzName ); } else if( null != jcbi && jcbi.userParamName.equals( cArgName ) && ( !jcbi.setFuncProcessed || i == jcbi.setFuncUserParamIdx ) && cArgType.isPointer() && jcbi.userParamType.equals( cArgType.getTargetType() ) ) diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 047a637..2bc453d 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * @@ -50,6 +50,8 @@ import com.jogamp.gluegen.cgram.types.Type; import java.io.PrintWriter; import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -455,6 +457,114 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { return getArgumentName(i) + "_offset"; } + private static final boolean DEBUG_JAVACALLBACK = false; + + private final void emitJavaCallbackKeyClass(final String KeyClassName) { + unit.emitln(" private 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(" private final "+jType+" "+name+";"); + return true; + } else { + return false; + } + } ); + unit.emitln(" "+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("Long.valueOf( "+name+" ).hashCode();"); + } 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(" }"); + unit.emitln(); + } + private final void emitJavaCallbackUsrParamClass(final String UsrParamClassName) { + unit.emitln(" private static class "+UsrParamClassName+" {"); + unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); + unit.emitln(" final Object param;"); + unit.emitln(" final long nativeParam;"); + unit.emitln(" "+UsrParamClassName+"("+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 emitBody() { if (!emitBody) { @@ -474,37 +584,68 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { unit.emitln(" }"); } if( null != javaCallback && !isPrivateNativeMethod ) { + final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); + final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); unit.emitln(); - final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - unit.emit(" public boolean is"+getInterfaceName()+"Mapped(final Object "+userParamArgName+")"); if( isInterface() ) { - unit.emitln(";"); + unit.emitln(" public boolean is"+capIfaceName+"Mapped("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" public "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" public Object get"+capIfaceName+"UserParam("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); } else { - unit.emitln(" {"); - unit.emitln(" return null != "+javaCallback.cbFuncTypeName+"UsrMap.get("+userParamArgName+");"); + final String usrMapInstanceName = lowIfaceName+"UsrMap"; + final boolean customKeyClass; + final String KeyClassName; + if( null != javaCallback.setFuncKeyClassName ) { + customKeyClass = true;; + KeyClassName = javaCallback.setFuncKeyClassName; + } else { + customKeyClass = false; + KeyClassName = CodeGenUtils.capitalizeString(capIfaceName+"Key"); + } + final String UsrParamClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"UserParam" ); + final String fqUsrParamClassName = cfg.packageName()+"."+cfg.className()+"."+UsrParamClassName; + unit.emitln(" public final boolean is"+capIfaceName+"Mapped("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" return null != "+usrMapInstanceName+".get(key);"); unit.emitln(" }"); - unit.emitln(" private final void release"+getInterfaceName()+"(final Object "+userParamArgName+") {"); - unit.emitln(" final "+javaCallback.cbFuncTypeName+"UserParam v = "+javaCallback.cbFuncTypeName+"UsrMap.remove("+userParamArgName+");"); - // unit.emitln(" System.err.println(\"ZZZ Release v \"+v+\", v.nativeParam 0x\"+Long.toHexString(null!=v?v.nativeParam:0));"); - unit.emitln(" if( null != v ) {"); - unit.emitln(" release"+getInterfaceName()+"Impl(v.nativeParam);"); - unit.emitln(" }"); + unit.emitln(" public final "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.func : null;"); + unit.emitln(" }"); + unit.emitln(" public final Object get"+capIfaceName+"UserParam("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.param : null;"); + unit.emitln(" }"); + unit.emitln(" private final void add"+capIfaceName+"Map("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+UsrParamClassName+" value) {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" "+usrMapInstanceName+".put(key, value);"); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" System.err.println(\"ZZZ Map \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); + } unit.emitln(" }"); - unit.emitln(" private static class "+javaCallback.cbFuncTypeName+"UserParam {"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final Object param;"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final long nativeParam;"); - unit.emitln(" "+javaCallback.cbFuncTypeName+"UserParam("+javaCallback.cbFuncTypeName+" func, Object param, long nativeParam) {"); - unit.emitln(" this.func = func;"); - unit.emitln(" this.param = param;"); - unit.emitln(" this.nativeParam = nativeParam;"); + unit.emitln(" private final void release"+capIfaceName+"Map("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".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+"MapImpl(value.nativeParam);"); unit.emitln(" }"); unit.emitln(" }"); - unit.emitln(" private final java.util.Map<Object, "+javaCallback.cbFuncTypeName+"UserParam> "+javaCallback.cbFuncTypeName+"UsrMap = new java.util.HashMap<Object, "+javaCallback.cbFuncTypeName+"UserParam>();"); - unit.emitln(" private native void release"+getInterfaceName()+"Impl(long nativeUserParam);"); + unit.emitln(" private native void release"+capIfaceName+"MapImpl(long nativeUserParam);"); + unit.emitln(); + if( !customKeyClass ) { + emitJavaCallbackKeyClass(KeyClassName); + } + if( !cfg.emittedJavaCallbackUserParamClasses.contains(fqUsrParamClassName) ) { + emitJavaCallbackUsrParamClass(UsrParamClassName); + cfg.emittedJavaCallbackUserParamClasses.add(fqUsrParamClassName); + } + unit.emitln(" private final java.util.Map<"+KeyClassName+", "+UsrParamClassName+"> "+usrMapInstanceName+" = new java.util.HashMap<"+KeyClassName+", "+UsrParamClassName+">();"); + unit.emitln(); } } } @@ -620,14 +761,18 @@ 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 userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - unit.emitln(" release"+getInterfaceName()+"("+userParamArgName+"); // Ensure a previously mapped instance is released"); - unit.emitln(" final long[] nativeUserParam = { 0 };"); + final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); + final String usrMapInstanceName = lowIfaceName+"UsrMap"; + unit.emitln(" synchronized( "+usrMapInstanceName+" ) {"); + unit.emitln(" final long[] nativeUserParam = { 0 };"); + unit.emitln(" release"+capIfaceName+"Map("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+"); // Ensure a previously mapped instance is released"); + unit.emitln(); } if (!returnType.isVoid()) { - unit.emit(" "); + unit.emit(" "); if (returnType.isCompoundTypeWrapper() || returnType.isNIOBuffer()) { unit.emitln("final ByteBuffer _res;"); @@ -645,9 +790,9 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } if (needsResultAssignment) { - unit.emit(" _res = "); + unit.emit(" _res = "); } else { - unit.emit(" "); + unit.emit(" "); if (!returnType.isVoid()) { unit.emit("return "); } @@ -659,10 +804,16 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { if( null != javaCallback ) { final String funcArgName = binding.getArgumentName(javaCallback.setFuncCBParamIdx); final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - // unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam \"+nativeUserParam[0]);"); - unit.emitln(" if( 0 != nativeUserParam[0] ) {"); - unit.emitln(" "+javaCallback.cbFuncTypeName+"UsrMap.put("+userParamArgName+", new "+javaCallback.cbFuncTypeName+"UserParam("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); - unit.emitln(" }"); + final String UsrParamClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"UserParam" ); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam 0x\"+Long.toHexString(nativeUserParam[0]));"); + } + unit.emitln(); + unit.emitln(" if( 0 != nativeUserParam[0] ) {"); + unit.emitln(" add"+capIfaceName+"Map("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+ + "new "+UsrParamClassName+"("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); + unit.emitln(" }"); + unit.emitln(" } // synchronized "); } emitPostCallCleanup(binding); |