From 6591f1fef419841660311bbb554aeda7b267c9a7 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Thu, 29 Jun 2023 03:50:12 +0200
Subject: GlueGen JavaCallback: 1st Working Draft: Changed 'JavaCallbackDef'
 config, added code generation incl. native to Java dispatch and resource
 management

Tested via Test4JavaCallback.java (using test2.[hc]).

Please read the GlueGen_Mapping.md as well as Test4JavaCallback.java .

+++

Some implementation details:

JavaConfiguration maps JavaCallbackDef to JavaCallback set-function and maintains a list.
JavaCallbackDef itself holds all configured details.

JavaConfiguration also maps JavaCallbackInfo to JavaCallback set-function.
JavaCallbackInfo itself holds all compile time information, as produced by JavaEmitter.beginFunctions(..).
This extends JavaCallbackDef and avoid repetetive computation for the callback-function-type and its MethodBinding,
parameter indices for the callback interface and userParam, etc.

CMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallback setter function creates a native 'UserParam' struct instance,
  which holds the callback-interface-jobject, its callback-jmethodID and
  the userParam-jobject for invocation of the actual JavaCallback interface method.

- To produce the C-Type -> JNI-Type conversion, An internal CMethodBindingEmitter instance
  for the native-callback function binding is created inside the CMethodBindingEmitter of the callback setter method.

  It is being used to map the types to JNI within the generated native callback function,
  passed to the actual JavaCallback method.

JavaMethodBindingEmitter: Native callback to Java dispatch
- The JavaCallbacl setter passes the callback-interface-object, the userParam-object and the
  callback-method-signature (to have the native method retrieve the jmethodID).

- It receives the native pointer of the native `UserParam` struct instance,
  which gets mapped to the userParam-object. (*TODO: Refine ownership + release*).
---
 .../jogamp/gluegen/JavaMethodBindingEmitter.java   | 66 +++++++++++++++++++---
 1 file changed, 59 insertions(+), 7 deletions(-)

(limited to 'src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java')

diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
index 74e18e5..1469b4f 100644
--- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
@@ -40,6 +40,7 @@
 
 package com.jogamp.gluegen;
 
+import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo;
 import com.jogamp.gluegen.cgram.HeaderParser;
 import com.jogamp.gluegen.cgram.types.AliasedSymbol;
 import com.jogamp.gluegen.cgram.types.ArrayType;
@@ -94,6 +95,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
   private String returnedArrayLengthExpression;
   private boolean returnedArrayLengthExpressionOnlyForComments = false;
 
+  private final JavaCallbackInfo javaCallback;
+
   // A suffix used to create a temporary outgoing array of Buffers to
   // represent an array of compound type wrappers
   private static final String COMPOUND_ARRAY_SUFFIX = "_buf_array_copy";
@@ -131,6 +134,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
     } else {
       setCommentEmitter(defaultInterfaceCommentEmitter);
     }
+    javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName());
     // !forImplementingMethodCall && !isInterface
   }
 
@@ -152,6 +156,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
     epilogue                      = arg.epilogue;
     returnedArrayLengthExpression = arg.returnedArrayLengthExpression;
     returnedArrayLengthExpressionOnlyForComments = arg.returnedArrayLengthExpressionOnlyForComments;
+    javaCallback                  = arg.javaCallback;
   }
 
   public boolean isNativeMethod() { return isNativeMethod; }
@@ -411,17 +416,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
       }
     }
     if( hasModifier(JavaMethodBindingEmitter.NATIVE) &&
-            null != cfg.bindingToJavaCallbackMap.get(binding.getName()) ) {
+        null != javaCallback )
+    {
         if (needComma) {
             unit.emit(", ");
         }
-        unit.emit("String callbackSignature");
+        unit.emit("String callbackSignature, long[/*1*/] nativeUserParam");
         ++numEmitted;
     }
     return numEmitted;
   }
 
-
   protected String getNativeImplMethodName() {
     return binding.getImplName() + ( useNIODirectOnly ? "0" : "1" );
   }
@@ -468,6 +473,40 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
       }
       unit.emitln("  }");
     }
+    if( null != javaCallback && !isPrivateNativeMethod ) {
+        unit.emitln();
+        final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx);
+        unit.emit("  public boolean is"+getInterfaceName()+"Mapped(final Object "+userParamArgName+")");
+        if( isInterface() ) {
+            unit.emitln(";");
+        } else {
+            unit.emitln(" {");
+            unit.emitln("    return null != "+javaCallback.cbFuncTypeName+"UsrMap.get("+userParamArgName+");");
+            unit.emitln("  }");
+            unit.emitln("  private final void release"+getInterfaceName()+"(final Object "+userParamArgName+") {");
+            unit.emitln("    final "+javaCallback.cbFuncTypeName+"UserParam v = "+javaCallback.cbFuncTypeName+"UsrMap.remove("+userParamArgName+");");
+            // unit.emitln("    System.err.println(\"ZZZ Release v \"+v+\", v.nativeParam 0x\"+Long.toHexString(null!=v?v.nativeParam:0));");
+            unit.emitln("    if( null != v ) {");
+            unit.emitln("      release"+getInterfaceName()+"Impl(v.nativeParam);");
+            unit.emitln("    }");
+            unit.emitln("  }");
+            unit.emitln("  private static class "+javaCallback.cbFuncTypeName+"UserParam {");
+            unit.emitln("    @SuppressWarnings(\"unused\")");
+            unit.emitln("    final "+javaCallback.cbFuncTypeName+" func;");
+            unit.emitln("    @SuppressWarnings(\"unused\")");
+            unit.emitln("    final Object param;");
+            unit.emitln("    @SuppressWarnings(\"unused\")");
+            unit.emitln("    final long nativeParam;");
+            unit.emitln("    "+javaCallback.cbFuncTypeName+"UserParam("+javaCallback.cbFuncTypeName+" func, Object param, long nativeParam) {");
+            unit.emitln("      this.func = func;");
+            unit.emitln("      this.param = param;");
+            unit.emitln("      this.nativeParam = nativeParam;");
+            unit.emitln("    }");
+            unit.emitln("  }");
+            unit.emitln("  private final java.util.Map<Object, "+javaCallback.cbFuncTypeName+"UserParam> "+javaCallback.cbFuncTypeName+"UsrMap = new java.util.HashMap<Object, "+javaCallback.cbFuncTypeName+"UserParam>();");
+            unit.emitln("  private native void release"+getInterfaceName()+"Impl(long nativeUserParam);");
+        }
+    }
   }
 
   protected void emitPrologueOrEpilogue(final List<String> code) {
@@ -579,11 +618,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
 
 
   protected void emitReturnVariableSetupAndCall(final MethodBinding binding) {
-    unit.emit("    ");
     final JavaType returnType = binding.getJavaReturnType();
     boolean needsResultAssignment = false;
 
+    if( null != javaCallback ) {
+        unit.emitln("    final long[] nativeUserParam = { 0 };");
+    }
     if (!returnType.isVoid()) {
+      unit.emit("    ");
       if (returnType.isCompoundTypeWrapper() ||
           returnType.isNIOBuffer()) {
         unit.emitln("final ByteBuffer _res;");
@@ -612,6 +654,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
     emitCall(binding);
     unit.emitln();
 
+    if( null != javaCallback ) {
+        final String funcArgName = binding.getArgumentName(javaCallback.setFuncCBParamIdx);
+        final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx);
+        // unit.emitln("    System.err.println(\"ZZZ returned nativeUserParam \"+nativeUserParam[0]);");
+        unit.emitln("    if( 0 == nativeUserParam[0] ) {");
+        unit.emitln("        release"+getInterfaceName()+"("+userParamArgName+");");
+        unit.emitln("    } else {");
+        unit.emitln("        "+javaCallback.cbFuncTypeName+"UsrMap.put("+userParamArgName+", new "+javaCallback.cbFuncTypeName+"UserParam("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));");
+        unit.emitln("    }");
+    }
+
     emitPostCallCleanup(binding);
     emitPrologueOrEpilogue(epilogue);
     if (needsResultAssignment) {
@@ -733,12 +786,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
       needComma = true;
       ++numArgsEmitted;
     }
-    final JavaConfiguration.JavaCallback jcb = cfg.bindingToJavaCallbackMap.get(binding.getName());
-    if( null != jcb ) {
+    if( null != javaCallback ) {
         if (needComma) {
             unit.emit(", ");
         }
-        unit.emit("\"" + jcb.methodSignature + "\"");
+        unit.emit("\"" + javaCallback.cbMethodSignature + "\", nativeUserParam");
         ++numArgsEmitted;
     }
     return numArgsEmitted;
-- 
cgit v1.2.3