summaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/gluegen/JavaEmitter.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-06-17 01:35:28 +0200
committerSven Gothel <[email protected]>2014-06-17 01:35:28 +0200
commitc3054a01990e55ab35756ea23ab7d7c05f24dd37 (patch)
tree96bd10e51cafe024a56d7b95594c7364d929c768 /src/java/com/jogamp/gluegen/JavaEmitter.java
parent9d857ea3575ee263801741a95711d9214c156276 (diff)
GlueGen: Add support for 'compound call-by-value', i.e. passing and returning struct instance
'compound call-by-value' is not efficient. However, to allow mapping APIs utilizing passing small structs as arguments and return values - this feature has been added. +++ To return the struct value, native code needs to allocate a NIO ByteBuffer and copy the data. The stack return value can be dismissed afterwards and the NIO buffer is returned. We include this functionality for all generated [impl] classes, native method: 'static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity)' (See: 'JavaEmitter.initClassAccessCode') Since this code requires knowledge of java classes and methods, for which a reference needs to be acquired, a static initialization method has been introduced for all generated [impl] classes: 'private static native boolean initializeImpl();' Per default the this method will be called in the new static initializer block of the class, which can be supressed via the configuration element: 'ManualStaticInit <class-name>' 'ManualStaticInit' can be used to issue the 'initializeImpl()' call in a custom static initializer written by the user. However, at the time 'initializeImpl()' gets called the JNI native library must have been loaded, of course! +++ - See tag: // FIXME: Compound call-by-value for code changes and validation of completeness Trigger for compond call-by-value in CMethodBindingEmitter is: !cArgType.isPointer() && javaArgType.isCompoundTypeWrapper() Trigger for compond call-by-value in JavaEmitter is: t.isCompound() +++ Further more we do tolerate 'javaType.isCPrimitivePointerType()', i.e. adding comments for offset/size and field entries, which are all NOP. This allows to utilize the remaining fields of the native structure. +++ Tests: Added call-by-value to test1.[ch] binding test!
Diffstat (limited to 'src/java/com/jogamp/gluegen/JavaEmitter.java')
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java140
1 files changed, 122 insertions, 18 deletions
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index f37f5a3..66e9b24 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -54,7 +54,6 @@ import java.nio.Buffer;
import java.util.logging.Logger;
import jogamp.common.os.MachineDescriptionRuntime;
-
import static java.util.logging.Level.*;
import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*;
@@ -349,6 +348,15 @@ public class JavaEmitter implements GlueEmitter {
"\" cannot be assigned to a int, long, float, or double");
}
+ /** Mangle a class, package or function name for JNI usage, i.e. replace all '.' w/ '_' */
+ protected static String jniMangle(String name) {
+ return name.replaceAll("_", "_1").replace('.', '_');
+ }
+ /** Returns the JNI method prefix consisting our of mangled package- and class-name */
+ protected static String getJNIMethodNamePrefix(final String javaPackageName, final String javaClassName) {
+ return "Java_"+jniMangle(javaPackageName)+"_"+jniMangle(javaClassName);
+ }
+
@Override
public void emitDefine(ConstantDefinition def, String optionalComment) throws Exception {
@@ -855,7 +863,7 @@ public class JavaEmitter implements GlueEmitter {
}
jniWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c", containingTypeName);
CodeGenUtils.emitAutogeneratedWarning(jniWriter, this);
- emitCHeader(jniWriter, containingTypeName);
+ emitCHeader(jniWriter, structClassPkg, containingTypeName);
} else {
jniWriter = null;
}
@@ -903,7 +911,7 @@ public class JavaEmitter implements GlueEmitter {
javaWriter.println(" private static final int mdIdx = MachineDescriptionRuntime.getStatic().ordinal();");
javaWriter.println();
// generate all offset and size arrays
- generateOffsetAndSizeArrays(javaWriter, containingTypeName, structType, null); /* w/o offset */
+ generateOffsetAndSizeArrays(javaWriter, " ", containingTypeName, structType, null); /* w/o offset */
for (int i = 0; i < structType.getNumFields(); i++) {
final Field field = structType.getField(i);
final Type fieldType = field.getType();
@@ -922,14 +930,14 @@ public class JavaEmitter implements GlueEmitter {
field + "\" in type \"" + name + "\")");
}
- generateOffsetAndSizeArrays(javaWriter, fieldName, fieldType, field);
+ generateOffsetAndSizeArrays(javaWriter, " ", fieldName, fieldType, field);
} else if (fieldType.isArray()) {
Type baseElementType = field.getType().asArray().getBaseElementType();
if(!baseElementType.isPrimitive())
break;
- generateOffsetAndSizeArrays(javaWriter, fieldName, null, field); /* w/o size */
+ generateOffsetAndSizeArrays(javaWriter, " ", fieldName, null, field); /* w/o size */
} else {
JavaType externalJavaType = null;
try {
@@ -941,10 +949,13 @@ public class JavaEmitter implements GlueEmitter {
}
if (externalJavaType.isPrimitive()) {
// Primitive type
- generateOffsetAndSizeArrays(javaWriter, fieldName, null, field); /* w/o size */
+ generateOffsetAndSizeArrays(javaWriter, " ", fieldName, null, field); /* w/o size */
+ } else if (externalJavaType.isCPrimitivePointerType()) {
+ // FIXME: Primitive Pointer type
+ generateOffsetAndSizeArrays(javaWriter, "//", fieldName, fieldType, field);
} else {
// FIXME
- LOG.log(WARNING, "Complicated fields (field \"{0}\" of type \"{1}\") not implemented yet", new Object[]{field, name});
+ LOG.log(WARNING, "Complicated fields (field \"{0}\" of type \"{1}\") not implemented yet: "+externalJavaType.getDumpString(), new Object[]{field, name});
// throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t +
// "\") not implemented yet");
}
@@ -952,7 +963,10 @@ public class JavaEmitter implements GlueEmitter {
}
}
javaWriter.println();
-
+ if (needsNativeCode) {
+ emitJavaInitCode(javaWriter, containingTypeName);
+ javaWriter.println();
+ }
javaWriter.println(" public static int size() {");
javaWriter.println(" return "+containingTypeName+"_size[mdIdx];");
javaWriter.println(" }");
@@ -1050,8 +1064,7 @@ public class JavaEmitter implements GlueEmitter {
}
} else if (fieldType.isCompound()) {
// FIXME: will need to support this at least in order to
- // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate
- // a name?)
+ // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate a name?)
if (fieldType.getName() == null) {
throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" +
field + "\" in type \"" + name + "\")");
@@ -1139,6 +1152,10 @@ public class JavaEmitter implements GlueEmitter {
javaWriter.println("accessor.get" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], MachineDescriptionRuntime.getStatic().md."+sizeDenominator+"SizeInBytes());");
}
javaWriter.println(" }");
+ } else if (javaType.isCPrimitivePointerType()) {
+ // FIXME: Primitive Pointer type
+ javaWriter.println();
+ javaWriter.println(" /** "+fieldName +": "+javaType.getDumpString()+" */");
}
}
}
@@ -1191,9 +1208,9 @@ public class JavaEmitter implements GlueEmitter {
writer.print(" public " + (abstractMethod ? "abstract " : "") + returnTypeName + " set" + capitalizedFieldName + "(" + paramTypeName + " val)");
}
- private void generateOffsetAndSizeArrays(PrintWriter writer, String fieldName, Type fieldType, Field field) {
+ private void generateOffsetAndSizeArrays(PrintWriter writer, String prefix, String fieldName, Type fieldType, Field field) {
if(null != field) {
- writer.print(" private static final int[] "+fieldName+"_offset = new int[] { ");
+ writer.print(prefix+"private static final int[] "+fieldName+"_offset = new int[] { ");
for( int i=0; i < machDescTargetConfigs.length; i++ ) {
if(0<i) {
writer.print(", ");
@@ -1204,7 +1221,7 @@ public class JavaEmitter implements GlueEmitter {
writer.println(" };");
}
if(null!=fieldType) {
- writer.print(" private static final int[] "+fieldName+"_size = new int[] { ");
+ writer.print(prefix+"private static final int[] "+fieldName+"_size = new int[] { ");
for( int i=0; i < machDescTargetConfigs.length; i++ ) {
if(0<i) {
writer.print(", ");
@@ -1308,7 +1325,7 @@ public class JavaEmitter implements GlueEmitter {
} else if (targetType.isDouble()) {
return JavaType.createForCDoublePointer();
} else if (targetType.isCompound()) {
- if (t.isArray()) {
+ if (t.isArray()) { // FIXME: Compound and Compound-Arrays
throw new RuntimeException("Arrays of compound types not handled yet");
}
// Special cases for known JNI types (in particular for converting jawt.h)
@@ -1316,7 +1333,6 @@ public class JavaEmitter implements GlueEmitter {
t.getName().equals("jobject")) {
return javaType(java.lang.Object.class);
}
-
String name = targetType.getName();
if (name == null) {
// Try containing pointer type for any typedefs
@@ -1393,6 +1409,12 @@ public class JavaEmitter implements GlueEmitter {
t.arrayDimension() + " targetType=\"" + targetType + "\"]");
}
+ } else if(t.isCompound() ) { // FIXME: Compound and Compound-Arrays
+ final String name = t.getName();
+ if (name == null) {
+ throw new RuntimeException("Couldn't find a proper type name for pointer type " + t);
+ }
+ return JavaType.createForCStruct(cfg.renameJavaType(name));
} else {
throw new RuntimeException(
"Could not convert C type \"" + t + "\" (class " +
@@ -1673,7 +1695,8 @@ public class JavaEmitter implements GlueEmitter {
}
if (cfg.emitImpl()) {
- emitCHeader(cWriter(), cfg.implClassName());
+ emitJavaInitCode();
+ emitCHeader(cWriter(), getImplPackageName(), cfg.implClassName());
}
} catch (Exception e) {
throw new RuntimeException(
@@ -1684,22 +1707,103 @@ public class JavaEmitter implements GlueEmitter {
}
- protected void emitCHeader(PrintWriter cWriter, String className) {
+ private static final String initClassAccessCode = "\n"+
+ "static const char * clazzNameBuffers = \"com/jogamp/common/nio/Buffers\";\n"+
+ "static const char * clazzNameBuffersStaticNewCstrName = \"newDirectByteBuffer\";\n"+
+ "static const char * clazzNameBuffersStaticNewCstrSignature = \"(I)Ljava/nio/ByteBuffer;\";\n"+
+ "static jclass clazzBuffers = NULL;\n"+
+ "static jmethodID cstrBuffersNew = NULL;\n"+
+ "\n"+
+ "static jboolean _initClazzAccess(JNIEnv *env) {\n"+
+ " jclass c;\n"+
+ "\n"+
+ " if(NULL!=cstrBuffersNew) return JNI_TRUE;\n"+
+ "\n"+
+ " c = (*env)->FindClass(env, clazzNameBuffers);\n"+
+ " if(NULL==c) {\n"+
+ " fprintf(stderr, \"FatalError: Can't find %s\\n\", clazzNameBuffers);\n"+
+ " (*env)->FatalError(env, clazzNameBuffers);\n"+
+ " return JNI_FALSE;\n"+
+ " }\n"+
+ " clazzBuffers = (jclass)(*env)->NewGlobalRef(env, c);\n"+
+ " if(NULL==clazzBuffers) {\n"+
+ " fprintf(stderr, \"FatalError: Can't use %s\\n\", clazzNameBuffers);\n"+
+ " (*env)->FatalError(env, clazzNameBuffers);\n"+
+ " return JNI_FALSE;\n"+
+ " }\n"+
+ "\n"+
+ " cstrBuffersNew = (*env)->GetStaticMethodID(env, clazzBuffers,\n"+
+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+
+ " if(NULL==cstrBuffersNew) {\n"+
+ " fprintf(stderr, \"FatalError: Java_jogamp_common_jvm_JVMUtil:: can't create %s.%s %s\\n\",\n"+
+ " clazzNameBuffers,\n"+
+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+
+ " (*env)->FatalError(env, clazzNameBuffersStaticNewCstrName);\n"+
+ " return JNI_FALSE;\n"+
+ " }\n"+
+ " return JNI_TRUE;\n"+
+ "}\n"+
+ "\n"+
+ "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity) {\n"+
+ " jobject jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, capacity);\n"+
+ " void * byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+
+ " memcpy(byteBufferPtr, source_address, capacity);\n"+
+ " return jbyteBuffer;\n"+
+ "}\n"+
+ "\n";
+
+ private static final String staticClassInitCode = "\n"+
+ " static {\n"+
+ " if( !initializeImpl() ) {\n"+
+ " throw new RuntimeException(\"Initialization failure\");\n"+
+ " }\n"+
+ " }\n"+
+ "\n";
+
+ protected void emitCHeader(PrintWriter cWriter, String packageName, String className) {
cWriter.println("#include <jni.h>");
cWriter.println("#include <stdlib.h>");
+ cWriter.println("#include <string.h>");
cWriter.println();
if (getConfig().emitImpl()) {
cWriter.println("#include <assert.h>");
cWriter.println();
}
-
+ emitCInitCode(cWriter, packageName, className);
for (String code : cfg.customCCode()) {
cWriter.println(code);
}
cWriter.println();
}
+ protected void emitCInitCode(PrintWriter cWriter, String packageName, String className) {
+ if (getConfig().emitImpl()) {
+ cWriter.println(initClassAccessCode);
+ cWriter.println("JNIEXPORT jboolean JNICALL "+JavaEmitter.getJNIMethodNamePrefix(packageName, className)+"_initializeImpl(JNIEnv *env, jclass _unused) {");
+ cWriter.println(" return _initClazzAccess(env);");
+ cWriter.println("}");
+ cWriter.println();
+ }
+ }
+ protected void emitJavaInitCode() {
+ if ( cfg.allStatic() ) {
+ emitJavaInitCode( javaWriter(), cfg.className() );
+ } else if( cfg.emitImpl() ) {
+ emitJavaInitCode( javaImplWriter(), cfg.implClassName() );
+ }
+ }
+ protected void emitJavaInitCode(PrintWriter jWriter, String className) {
+ if( null != jWriter ) {
+ jWriter.println();
+ jWriter.println(" private static native boolean initializeImpl();");
+ jWriter.println();
+ if( !cfg.manualStaticInit(className) ) {
+ jWriter.println(staticClassInitCode);
+ }
+ }
+ }
+
/**
* Write out any footer information for the output files (closing brace of
* class definition, etc).