aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-06-29 02:18:18 +0200
committerSven Gothel <[email protected]>2023-06-29 02:18:18 +0200
commitce542531f4cb6fe2bf37f3926ea869722e5acf7d (patch)
tree12743ccbf43fec5a5d8da2a5abba1e5831cc80cd
parent9f69f8b60b9fdc80076e20f303252ddd35b97ccf (diff)
GlueGen LibraryOnLoad Config: Generate `JNI_OnLoad(..)` for dynamic and `JNI_OnLoad_<LibraryBasename>(..)` for static libraries and `JVMUtil_GetJNIEnv(..)` to resolve the `JNIEnv*` as used by JavaCallback
-rw-r--r--doc/GlueGen_Mapping.md17
-rw-r--r--src/java/com/jogamp/gluegen/CCodeUnit.java109
-rw-r--r--src/java/com/jogamp/gluegen/JavaConfiguration.java12
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java8
4 files changed, 141 insertions, 5 deletions
diff --git a/doc/GlueGen_Mapping.md b/doc/GlueGen_Mapping.md
index 5503db3..ee23845 100644
--- a/doc/GlueGen_Mapping.md
+++ b/doc/GlueGen_Mapping.md
@@ -36,6 +36,8 @@ GlueGen also supports [producing an OO-Style API mapping](#oo-style-api-interfac
GlueGen is capable to bind low-level APIs such as the Java Native Interface (JNI) and
the AWT Native Interface (JAWT) back up to the Java programming language.
+Further, GlueGen supports generating `JNI_OnLoad(..)` for dynamic and `JNI_OnLoad_<LibraryBasename>(..)` for static libraries via [`LibraryOnLoad Bindingtest2`](#libraryonload-librarybasename-for-jni_onload-), which also provides `JVMUtil_GetJNIEnv(..)` to resolve the `JNIEnv*` as used by [Java callback methods](#java-callback-from-native-c-api-support).
+
GlueGen utilizes [JCPP](https://jogamp.org/cgit/jcpp.git/about/), migrated C preprocessor written in Java.
GlueGen is used for the [JogAmp](https://jogamp.org) projects
@@ -794,7 +796,20 @@ public interface Bindingtest2 {
*TODO: Work in progress*
-#### Example
+## Misc Configurations
+
+### `LibraryOnLoad <LibraryBasename>` for `JNI_OnLoad*(..)` ...
+
+`LibraryOnLoad <LibraryBasename>` generates native JNI code `JNI_OnLoad(..)` used for dynamic libraries,
+`JNI_OnLoad_<LibraryBasename>(..)` used for static libraries,
+`JVMUtil_GetJNIEnv(..)` and the instance of `JavaVM* <LibraryBasename>_jvmHandle`.
+
+The `JNI_OnLoad*(..)` methods set the `JavaVM* <LibraryBasename>_jvmHandle`, which in turn is utilized by
+`JVMUtil_GetJNIEnv(..)` to attach a new thread to the `JavaVM*` generating a new `JNIEnv*`in daemon mode -
+or just to retrieve the thread's `JNIEnv*`, if already attached to the `JavaVM*`.
+
+The `LibraryBasename` parameter is used to generate the `JNI_OnLoad_<LibraryBasename>(..)` variant for statically linked libraries.
+`JNI_OnLoad(..)`, `JNI_OnLoad_<LibraryBasename>(..)` and `JVMUtil_GetJNIEnv(..)`
## Platform Header Files
diff --git a/src/java/com/jogamp/gluegen/CCodeUnit.java b/src/java/com/jogamp/gluegen/CCodeUnit.java
index df5c6e7..5c0db27 100644
--- a/src/java/com/jogamp/gluegen/CCodeUnit.java
+++ b/src/java/com/jogamp/gluegen/CCodeUnit.java
@@ -50,13 +50,16 @@ public class CCodeUnit extends CodeUnit {
CodeGenUtils.emitAutogeneratedWarning(output, generator, "C-Unit: "+cUnitName+", "+filename);
}
- public void emitHeader(final String packageName, final String className, final List<String> customCode) {
+ public void emitHeader(final String libraryBasename, final String packageName, final String className, final List<String> customCode) {
emitln("#include <jni.h>");
emitln("#include <stdlib.h>");
emitln("#include <string.h>");
emitln("#include <assert.h>");
emitln("#include <stddef.h>");
emitln();
+ if( null != libraryBasename && libraryBasename.length() > 0 ) {
+ emitJNIOnLoadJNIEnvDecl(libraryBasename);
+ }
emitln("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, jclass clazzBuffers, void * source_address, size_t capacity); /* forward decl. */");
emitln();
@@ -71,6 +74,16 @@ public class CCodeUnit extends CodeUnit {
}
}
+ /** Emits {@link #getJNIOnLoadJNIEnvDecl(String)}. */
+ public void emitJNIOnLoadJNIEnvDecl(final String libraryBasename) {
+ emitln( getJNIOnLoadJNIEnvDecl(libraryBasename) );
+ }
+
+ /** Emits {@link #getJNIOnLoadJNIEnvCode(String)}. */
+ public void emitJNIOnLoadJNIEnvCode(final String libraryBasename) {
+ emitln( getJNIOnLoadJNIEnvCode(libraryBasename) );
+ }
+
@Override
public String toString() { return "CCodeUnit[unit "+cUnitName+", file "+filename+"]"; }
@@ -94,4 +107,98 @@ public class CCodeUnit extends CodeUnit {
" }\n"+
" return jbyteBuffer;\n"+
"}\n";
+
+ /**
+ * Returns native JNI declarations for `JavaVM* {libraryBasename}_jvmHandle`
+ * and `JVMUtil_GetJNIEnv(..)`.
+ * <p>
+ * See {@link #getJNIOnLoadJNIEnvCode(String)} for details.
+ * </p>
+ * @param libraryBasename library basename to generate the `JNI_OnLoad_{libraryBasename}(..)` variant for statically linked libraries.
+ * @return the code
+ * @see #getJNIOnLoadJNIEnvCode(String)
+ */
+ public static final String getJNIOnLoadJNIEnvDecl(final String libraryBasename) {
+ return "extern JavaVM *"+libraryBasename+"_jvmHandle;\n"+
+ "JNIEnv* JVMUtil_GetJNIEnv();\n"+
+ "\n";
+ }
+ /**
+ * Returns native JNI code `JNI_OnLoad(..)` used for dynamic libraries,
+ * `JNI_OnLoad_{libraryBasename}(..)` used for static libraries,
+ * `JVMUtil_GetJNIEnv(..)` and the instance of `JavaVM* {libraryBasename}_jvmHandle`.
+ * <p>
+ * The `JNI_OnLoad*(..)` methods set the `JavaVM* {libraryBasename}_jvmHandle`,
+ * which in turn is utilized by `JVMUtil_GetJNIEnv(..)`
+ * to attach a new thread to the `JavaVM*` generating a new `JNIEnv*`in daemon mode -
+ * or just to retrieve the thread's `JNIEnv*`, if already attached to the `JavaVM*`.
+ * </p>
+ * @param libraryBasename library basename to generate the `JNI_OnLoad_{libraryBasename}(..)` variant for statically linked libraries.
+ * @return the code
+ * @see #getJNIOnLoadJNIEnvDecl(String)
+ */
+ public static final String getJNIOnLoadJNIEnvCode(final String libraryBasename) {
+ final String jvmHandleName = libraryBasename+"_jvmHandle";
+ final StringBuilder sb = new StringBuilder();
+ sb.append("JavaVM *").append(jvmHandleName).append(" = NULL;\n");
+ sb.append("JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *initVM, void *reserved) {\n");
+ sb.append(" (void)reserved;\n");
+ sb.append(" ").append(jvmHandleName).append(" = initVM;\n");
+ sb.append(" fprintf(stderr, \"ON_LOAD_0\\n\");\n");
+ sb.append(" return JNI_VERSION_1_8;\n");
+ sb.append("}\n");
+ sb.append("JNIEXPORT jint JNICALL JNI_OnLoad_").append(libraryBasename).append("(JavaVM *initVM, void *reserved) {\n");
+ sb.append(" (void)reserved;\n");
+ sb.append(" ").append(jvmHandleName).append(" = initVM;\n");
+ sb.append(" fprintf(stderr, \"ON_LOAD_1\\n\");\n");
+ sb.append(" return JNI_VERSION_1_8;\n");
+ sb.append("}\n");
+ sb.append("\n");
+ sb.append("JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {\n");
+ sb.append(" (void)vm;\n");
+ sb.append(" (void)reserved;\n");
+ sb.append(" fprintf(stderr, \"ON_UNLOAD_0\\n\");\n");
+ sb.append("}\n");
+ sb.append("\n");
+ sb.append("JNIEXPORT void JNICALL JNI_OnUnload_").append(libraryBasename).append("(JavaVM *vm, void *reserved) {\n");
+ sb.append(" (void)vm;\n");
+ sb.append(" (void)reserved;\n");
+ sb.append(" fprintf(stderr, \"ON_UNLOAD_1\\n\");\n");
+ sb.append("}\n");
+ sb.append("\n");
+ sb.append("JNIEnv* JVMUtil_GetJNIEnv() {\n");
+ sb.append(" JNIEnv* curEnv = NULL;\n");
+ sb.append(" JNIEnv* newEnv = NULL;\n");
+ sb.append(" int envRes;\n");
+ sb.append("\n");
+ sb.append(" if(NULL==").append(jvmHandleName).append(") {\n");
+ sb.append(" fprintf(stderr, \"JVMUtil_GetJNIEnv(").append(libraryBasename).append("): No JavaVM handle registered.\");\n");
+ sb.append(" return NULL;\n");
+ sb.append(" }\n");
+ sb.append("\n");
+ sb.append(" // retrieve this thread's JNIEnv curEnv - or detect it's detached\n");
+ sb.append(" envRes = (*").append(jvmHandleName).append(")->GetEnv(").append(jvmHandleName).append(", (void **) &curEnv, JNI_VERSION_1_8) ;\n");
+ sb.append(" if( JNI_EDETACHED == envRes ) {\n");
+ sb.append(" // detached thread - attach to JVM as daemon, w/o need to be detached!\n");
+ sb.append(" envRes = (*").append(jvmHandleName).append(")->AttachCurrentThreadAsDaemon(").append(jvmHandleName).append(", (void**) &newEnv, NULL);\n");
+ sb.append(" if( JNI_OK != envRes ) {\n");
+ sb.append(" fprintf(stderr, \"JVMUtil_GetJNIEnv(").append(libraryBasename).append("): Can't attach thread: %d\\n\", envRes);\n");
+ sb.append(" return NULL;\n");
+ sb.append(" }\n");
+ sb.append(" curEnv = newEnv;\n");
+ sb.append(" } else if( JNI_OK != envRes ) {\n");
+ sb.append(" // oops ..\n");
+ sb.append(" fprintf(stderr, \"JVMUtil_GetJNIEnv(").append(libraryBasename).append("): Can't GetEnv: %d\\n\", envRes);\n");
+ sb.append(" return NULL;\n");
+ sb.append(" }\n");
+ sb.append(" if (curEnv==NULL) {\n");
+ sb.append(" fprintf(stderr, \"JVMUtil_GetJNIEnv(").append(libraryBasename).append("): env is NULL\\n\");\n");
+ sb.append(" return NULL;\n");
+ sb.append(" }\n");
+ sb.append(" return curEnv;\n");
+ sb.append("}\n");
+ sb.append("\n");
+ return sb.toString();
+ }
}
+
diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java
index 30ccbb2..3faddcd 100644
--- a/src/java/com/jogamp/gluegen/JavaConfiguration.java
+++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java
@@ -62,6 +62,7 @@ import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*;
public class JavaConfiguration {
private int nestedReads;
+ private String libraryOnLoadName;
private String packageName;
private String implPackageName;
private String className;
@@ -284,6 +285,11 @@ public class JavaConfiguration {
public void setOutputRootDir(final String s) { outputRootDir=s; }
+ /** Returns the library basename used to {@link CCodeUnit#emitJNIOnLoadJNIEnvCode(String)}. */
+ public String libraryOnLoadName() {
+ return libraryOnLoadName;
+ }
+
/** Returns the package name parsed from the configuration file. */
public String packageName() {
return packageName;
@@ -1310,8 +1316,10 @@ public class JavaConfiguration {
protected void dispatch(final String cmd, final StringTokenizer tok, final File file, final String filename, final int lineNo) throws IOException {
//System.err.println("read cmd = [" + cmd + "]");
- if (cmd.equalsIgnoreCase("Package")) {
- packageName = readString("package", tok, filename, lineNo);
+ if (cmd.equalsIgnoreCase("LibraryOnLoad")) {
+ libraryOnLoadName = readString("LibraryOnLoad", tok, filename, lineNo);
+ } else if (cmd.equalsIgnoreCase("Package")) {
+ packageName = readString("Package", tok, filename, lineNo);
} else if (cmd.equalsIgnoreCase("GlueGenRuntimePackage")) {
gluegenRuntimePackage = readString("GlueGenRuntimePackage", tok, filename, lineNo);
} else if (cmd.equalsIgnoreCase("ImplPackage")) {
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index 3a86239..939ce92 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -2980,7 +2980,10 @@ public class JavaEmitter implements GlueEmitter {
}
if (cfg.emitImpl()) {
- cUnit().emitHeader(getImplPackageName(), cfg.implClassName(), cfg.customCCode());
+ if( !cfg.getJavaCallbackList().isEmpty() && null == cfg.libraryOnLoadName() ) {
+ LOG.log(WARNING, "JavaCallback used, but no 'LibraryOnLoad' basename specified for JNI_OnLoad(..). Exactly one native code-unit for the library must specify with 'LibraryOnLoad' basename");
+ }
+ cUnit().emitHeader(cfg.libraryOnLoadName(), getImplPackageName(), cfg.implClassName(), cfg.customCCode());
}
} catch (final Exception e) {
throw new RuntimeException(
@@ -3004,6 +3007,9 @@ public class JavaEmitter implements GlueEmitter {
javaImplUnit.emitTailCode();
javaImplUnit().emitln("} // end of class " + cfg.implClassName());
}
+ if (cfg.emitImpl() && null != cfg.libraryOnLoadName() ) {
+ cUnit.emitJNIOnLoadJNIEnvCode(cfg.libraryOnLoadName());
+ }
}
private JavaType javaType(final Class<?> c) {