aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-07-05 10:21:48 +0200
committerSven Gothel <[email protected]>2023-07-05 10:21:48 +0200
commit5ceca8550b82591a6a2661a26d3e0d5e6e3e15ff (patch)
tree95768138afdb5ee4c6d3b705e803ad497241f756
parentae4c2c3e59ed92caa6f0e18360b7236e50899bf6 (diff)
GlueGen: Add 'PascalString' string semantics (length + value-ptr), added prelim code for JavaCallback use-case emitBodyMapCToJNIType()
It is common in toolkit APIs that a string might not be passed as a 'nul' terminated (EOS) C string, but as a Pascal string with a given length argument. A C string is specied as ArgumentIsString alEventCallbackInject 3 while allowing multiple indices .. A Pascal string can be specified as ArgumentIsPascalString ALEVENTPROCSOFT 3 4 while allowing multiple indice-tuples for length and value .. The tuple consist of the length agrument-index first (usually an int) followed by the value argument-index (usually a 'char*'). +++ CMethodBindingEmitter.emitBodyMapCToJNIType(), where PascalString is implemented, is currently being used for - JNI return statement (no PascalString impact possible) - JavaCallback C type -> JNI type, PascalString impacting
-rwxr-xr-xdoc/manual/index.html13
-rw-r--r--src/java/com/jogamp/gluegen/CMethodBindingEmitter.java30
-rw-r--r--src/java/com/jogamp/gluegen/JavaConfiguration.java93
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java19
-rw-r--r--src/java/com/jogamp/gluegen/JavaType.java45
-rw-r--r--src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg2
6 files changed, 177 insertions, 25 deletions
diff --git a/doc/manual/index.html b/doc/manual/index.html
index 01449f7..b960b56 100755
--- a/doc/manual/index.html
+++ b/doc/manual/index.html
@@ -959,6 +959,19 @@
<code>byte[]</code> or <code>ByteBuffer</code>.
</dd>
+ <dt><strong><a name="ArgumentIsString">ArgumentIsPascalString</a></strong></dt>
+ <dd> Syntax: <code>ArgumentIsString [function name]
+ [indice-tuples...]</code>, with each tuple being index for the <code>int length</code>
+ and the <code>const char* value</code> argument with index 0 for the the first argument<br/>
+
+ (optional) For a C function with one or more outgoing
+ <code>int length</code> and <code>char* value</code> (or compatible data type) arguments,
+ indicates that those arguments are semantically non-null-terminated Pascal strings rather than
+ null-terminated C strings or arbitrary arrays of bytes. The generated glue code will be modified to
+ emit those arguments as java.lang.String objects rather than
+ <code>byte[]</code> or <code>ByteBuffer</code>.
+ </dd>
+
<dt><strong><a name="ClassJavadoc">ClassJavadoc</a></strong></dt>
<dd> Syntax: <code>ClassJavadoc [class name] [code...]</code> <br/>
diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java
index 9c7ed0b..e20a1cb 100644
--- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java
+++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java
@@ -1176,12 +1176,36 @@ public class CMethodBindingEmitter extends FunctionEmitter {
unit.emitln(" */");
}
} else if (javaType.isString()) {
+ final boolean pascalString = javaType.isPascalStringVariant();
+ final String lenArgName;
+ if( pascalString ) {
+ final int lenIdx = cfg.pascalStringLengthIndex(getCSymbol(), argIdx);
+ lenArgName = 0 <= lenIdx ? binding.getArgumentName(lenIdx) : null;
+ } else {
+ lenArgName = null;
+ }
if( addLocalVar ) {
- unit.emit(" "+javaType.jniTypeName()+" "+javaArgName+" = ");
+ unit.emitln(" "+javaType.jniTypeName()+" "+javaArgName+";");
+ }
+ if( null != lenArgName ) {
+ unit.emitln(" if (NULL == "+cArgName+" || 0 >= "+lenArgName+" ) {");
} else {
- unit.emit(" "+javaArgName+" = ");
+ unit.emitln(" if (NULL == "+cArgName+") {");
+ }
+ unit.emitln(" "+javaArgName+" = NULL;");
+ unit.emitln(" } else {");
+ if( null != lenArgName ) {
+ unit.emitln(" char* "+cArgName+"_cstr = calloc("+lenArgName+"+1, sizeof(char)); // PascalString -> Add EOS");
+ unit.emitln(" memcpy("+cArgName+"_cstr, "+cArgName+", "+lenArgName+");");
}
- unit.emitln("(NULL == "+cArgName+") ? NULL : (*env)->NewStringUTF(env, (const char *)"+cArgName+");");
+ unit.emit (" "+javaArgName+" = ");
+ if( null != lenArgName ) {
+ unit.emitln("(*env)->NewStringUTF(env, (const char *)"+cArgName+"_cstr);");
+ unit.emitln(" free("+cArgName+"_cstr);");
+ } else {
+ unit.emitln("(*env)->NewStringUTF(env, (const char *)"+cArgName+");");
+ }
+ unit.emitln(" }");
} else if (javaType.isArrayOfCompoundTypeWrappers() ||
( javaType.isArray() && javaType.isNIOByteBufferArray() ) )
{
diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java
index 10f43a7..5cea4c3 100644
--- a/src/java/com/jogamp/gluegen/JavaConfiguration.java
+++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java
@@ -144,12 +144,47 @@ public class JavaConfiguration {
private final Map<String, String> returnedArrayLengths = new HashMap<String, String>();
private final Set<String> maxOneElement = new HashSet<String>();
+ /** Pascal string argument index tuple for length and value. */
+ public static class PascalStringIdx {
+ public final int lengthIndex;
+ public final int valueIndex;
+
+ PascalStringIdx(final int lenIdx, final int valIdx) {
+ lengthIndex = lenIdx;
+ valueIndex = valIdx;
+ }
+
+ public void pushValueIndex(final List<Integer> indices) {
+ indices.add(valueIndex);
+ }
+ public static final List<Integer> pushValueIndex(final List<PascalStringIdx> source, List<Integer> indices) {
+ if( null == indices ) {
+ indices = new ArrayList<Integer>(2);
+ }
+ for(final PascalStringIdx p : source) {
+ p.pushValueIndex(indices);
+ }
+ return indices;
+ }
+
+ @Override
+ public String toString() {
+ return "PascalString[lenIdx "+lengthIndex+", valIdx "+valueIndex+"]";
+ }
+ }
+
/**
* Key is function that has some byte[] or short[] arguments that should be
* converted to String args; value is List of Integer argument indices
*/
private final Map<String, List<Integer>> argumentsAreString = new HashMap<String, List<Integer>>();
+ /**
+ * Key is function that has a pascal string, i.e. length and some byte[] or short[] arguments that should be
+ * converted to String args; value is a list of PascalStringArg
+ */
+ private final Map<String, List<PascalStringIdx>> argumentsArePascalString = new HashMap<String, List<PascalStringIdx>>();
+
/** JavaCallback configuration definition (static) */
public static class JavaCallbackDef {
final String cbFuncTypeName;
@@ -609,13 +644,6 @@ public class JavaConfiguration {
/** Returns a list of <code>Integer</code>s which are the indices of <code>const char*</code>
arguments that should be converted to <code>String</code>s. Returns null if there are no
- such hints for the given function name. */
- public List<Integer> stringArguments(final String functionName) {
- return argumentsAreString.get(functionName);
- }
-
- /** Returns a list of <code>Integer</code>s which are the indices of <code>const char*</code>
- arguments that should be converted to <code>String</code>s. Returns null if there are no
such hints for the given function alias symbol. */
public List<Integer> stringArguments(final AliasedSymbol symbol) {
final String name = symbol.getName();
@@ -632,6 +660,36 @@ public class JavaConfiguration {
return res;
}
+ /** Returns a list of PascalStringIdx which are tuples of indices of <code>int len, const char*</code>
+ arguments that should be converted to <code>String</code>s. Returns null if there are no
+ such hints for the given function alias symbol. */
+ public List<PascalStringIdx> pascalStringArgument(final AliasedSymbol symbol) {
+ final String name = symbol.getName();
+ final Set<String> aliases = symbol.getAliasedNames();
+
+ List<PascalStringIdx> res = argumentsArePascalString.get(name);
+ if( null == res ) {
+ res = oneInMap(argumentsArePascalString, aliases);
+ if( null == res ) {
+ return null;
+ }
+ }
+ LOG.log(INFO, getASTLocusTag(symbol), "ArgumentIsPascalString: {0} -> {1}", symbol, res);
+ return res;
+ }
+
+ public int pascalStringLengthIndex(final AliasedSymbol symbol, final int valueIndex) {
+ final List<PascalStringIdx> pascals = pascalStringArgument(symbol);
+ if( null != pascals ) {
+ for(final PascalStringIdx p : pascals) {
+ if( valueIndex == p.valueIndex ) {
+ return p.lengthIndex;
+ }
+ }
+ }
+ return -1;
+ }
+
public boolean isForceUsingNIOOnly4All() { return forceUseNIOOnly4All; }
public void addUseNIOOnly(final String fname ) {
@@ -1380,6 +1438,8 @@ public class JavaConfiguration {
readMaxOneElement(tok, filename, lineNo);
} else if (cmd.equalsIgnoreCase("ArgumentIsString")) {
readArgumentIsString(tok, filename, lineNo);
+ } else if (cmd.equalsIgnoreCase("ArgumentIsPascalString")) {
+ readArgumentIsPascalString(tok, filename, lineNo);
} else if (cmd.equalsIgnoreCase("JavaCallbackDef")) {
readJavaCallbackDef(tok, filename, lineNo);
} else if (cmd.equalsIgnoreCase("JavaCallbackKey")) {
@@ -1948,6 +2008,25 @@ public class JavaConfiguration {
}
}
+ protected void readArgumentIsPascalString(final StringTokenizer tok, final String filename, final int lineNo) {
+ try {
+ final String methodName = tok.nextToken();
+ final List<PascalStringIdx> pascalTuples = new ArrayList<PascalStringIdx>(2);
+ while (tok.countTokens() >= 2) {
+ final int lenIdx = Integer.valueOf(tok.nextToken()).intValue();
+ final int valIdx = Integer.valueOf(tok.nextToken()).intValue();
+ pascalTuples.add(new PascalStringIdx(lenIdx, valIdx));
+ }
+ if( pascalTuples.size() > 0 ) {
+ argumentsArePascalString.put(methodName, pascalTuples);
+ }
+ } catch (final NoSuchElementException e) {
+ throw new RuntimeException(
+ "Error parsing \"ArgumentIsPascalString\" command at line " + lineNo +
+ " in file \"" + filename + "\"", e);
+ }
+ }
+
protected void readStructPackage(final StringTokenizer tok, final String filename, final int lineNo) {
try {
final String struct = tok.nextToken();
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index 96202bf..8af79e4 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -78,6 +78,7 @@ import com.jogamp.common.util.HashUtil;
import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider;
import com.jogamp.gluegen.JavaConfiguration.JavaCallbackDef;
import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo;
+import com.jogamp.gluegen.JavaConfiguration.PascalStringIdx;
import com.jogamp.gluegen.Logging.LoggerIf;
import com.jogamp.gluegen.cgram.types.AliasedSymbol;
import com.jogamp.gluegen.cgram.types.ArrayType;
@@ -3049,6 +3050,9 @@ public class JavaEmitter implements GlueEmitter {
private JavaType javaType(final Class<?> c) {
return JavaType.createForClass(c);
}
+ private JavaType javaStringType(final Class<?> c, final boolean pascalString) {
+ return JavaType.createForStringClass(c, pascalString);
+ }
/** Maps the C types in the specified function to Java types through
the MethodBinding interface. Note that the JavaTypes in the
@@ -3081,7 +3085,7 @@ public class JavaEmitter implements GlueEmitter {
"\". ReturnsString requires native method to have return type \"char *\"",
sym.getASTLocusTag());
}
- javaReturnType = javaType(java.lang.String.class);
+ javaReturnType = javaStringType(java.lang.String.class, false);
} else {
final JavaType r = cfg.getOpaqueReturnType(sym);
if( null != r ) {
@@ -3094,7 +3098,11 @@ public class JavaEmitter implements GlueEmitter {
// List of the indices of the arguments in this function that should be
// converted from byte[] or short[] to String
final List<JavaType> javaArgumentTypes = new ArrayList<JavaType>();
- final List<Integer> stringArgIndices = cfg.stringArguments(sym);
+ List<Integer> stringArgIndices = cfg.stringArguments(sym);
+ final List<PascalStringIdx> pascalStringArgs = cfg.pascalStringArgument(sym);
+ if( null != pascalStringArgs ) {
+ stringArgIndices = PascalStringIdx.pushValueIndex(pascalStringArgs, stringArgIndices);
+ }
final JavaCallbackInfo jcbi = cfg.setFuncToJavaCallbackMap.get( sym.getName() );
int jcbiSetFuncCBParamIdx=-1, jcbiSetFuncUserParamIdx=-1;
@@ -3134,7 +3142,7 @@ public class JavaEmitter implements GlueEmitter {
mappedType = JavaType.forObjectClass();
}
}
- } else if (stringArgIndices != null && stringArgIndices.contains(i)) {
+ } 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 ");
if (mappedType.isCVoidPointerType() ||
@@ -3147,10 +3155,11 @@ public class JavaEmitter implements GlueEmitter {
// convert mapped type from:
// void*, byte[], and short[] to String
// ByteBuffer[] and ShortBuffer[] to String[]
+ final boolean pascalString = cfg.pascalStringLengthIndex(sym, i) >= 0;
if (mappedType.isArray() || mappedType.isNIOPointerBuffer()) {
- mappedType = javaType(ArrayTypes.stringArrayClass);
+ mappedType = javaStringType(ArrayTypes.stringArrayClass, pascalString);
} else {
- mappedType = javaType(String.class);
+ mappedType = javaStringType(String.class, pascalString);
}
}
else {
diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java
index df3432a..6fb9a17 100644
--- a/src/java/com/jogamp/gluegen/JavaType.java
+++ b/src/java/com/jogamp/gluegen/JavaType.java
@@ -65,6 +65,7 @@ public class JavaType {
private final Type elementType; // Element type if this JavaType represents a C array
private final C_PTR primitivePointerType;
private final boolean opaqued;
+ private final boolean pascalString;
private static JavaType objectType;
private static JavaType nioBufferType;
@@ -130,7 +131,7 @@ public class JavaType {
the emitters understand how to perform proper conversion from
the corresponding C type. */
public static JavaType createForOpaqueClass(final Class<?> clazz) {
- return new JavaType(clazz, true);
+ return new JavaType(clazz, true, false);
}
/** Creates a JavaType corresponding to the given Java type. This
@@ -138,7 +139,11 @@ public class JavaType {
the emitters understand how to perform proper conversion from
the corresponding C type. */
public static JavaType createForClass(final Class<?> clazz) {
- return new JavaType(clazz, false);
+ return new JavaType(clazz, false, false);
+ }
+
+ public static JavaType createForStringClass(final Class<?> clazz, final boolean pascalString) {
+ return new JavaType(clazz, false, pascalString);
}
/**
@@ -558,6 +563,12 @@ public class JavaType {
return (clazz == java.lang.String.class);
}
+ public boolean isPascalStringVariant() { return pascalString; }
+
+ public boolean isPascalString() {
+ return isString() && this.pascalString;
+ }
+
public boolean isArray() {
return ((clazz != null) && clazz.isArray());
}
@@ -590,6 +601,9 @@ public class JavaType {
return (clazz != null && clazz.isArray() && clazz.getComponentType() == java.lang.String.class);
}
+ public boolean isPascalStringArray() {
+ return isStringArray() && this.pascalString;
+ }
public boolean isPrimitive() {
return ((clazz != null) && !isArray() && clazz.isPrimitive() && (clazz != Void.TYPE));
@@ -678,7 +692,7 @@ public class JavaType {
@Override
public Object clone() {
- return new JavaType(primitivePointerType, clazz, clazzName, structName, elementType);
+ return new JavaType(primitivePointerType, clazz, clazzName, structName, elementType, pascalString);
}
@Override
@@ -729,7 +743,18 @@ public class JavaType {
if( isOpaqued() ) {
append(sb, "opaque", prepComma); prepComma=true;
}
- if( isArray() ) {
+ if( isString() ) {
+ if( pascalString ) {
+ sb.append("pascal ");
+ }
+ append(sb, "string", prepComma); prepComma=true;
+ }
+ if( isStringArray() ) {
+ if( pascalString ) {
+ sb.append("pascal ");
+ }
+ append(sb, "stringArray", prepComma); prepComma=true;
+ } else if( isArray() ) {
append(sb, "array", prepComma); prepComma=true;
}
if( isArrayOfCompoundTypeWrappers() ) {
@@ -738,9 +763,6 @@ public class JavaType {
if( isCompoundTypeWrapper() ) {
append(sb, "compound", prepComma); prepComma=true;
}
- if( isArray() ) {
- append(sb, "array", prepComma); prepComma=true;
- }
if( isPrimitive() ) {
append(sb, "primitive", prepComma); prepComma=true;
}
@@ -774,7 +796,7 @@ public class JavaType {
* Constructs a representation for a type corresponding to the given Class
* argument.
*/
- private JavaType(final Class<?> clazz, final boolean opaqued) {
+ private JavaType(final Class<?> clazz, final boolean opaqued, final boolean pascalString) {
if( null == clazz ) {
throw new IllegalArgumentException("null clazz passed");
}
@@ -784,6 +806,7 @@ public class JavaType {
this.structName = null;
this.elementType = null;
this.opaqued = opaqued;
+ this.pascalString = pascalString;
}
/** Constructs a type representing a either a named clazz or a named C struct.*/
@@ -804,6 +827,7 @@ public class JavaType {
this.clazz = null;
this.elementType = null;
this.opaqued = false;
+ this.pascalString = false;
}
/** Constructs a type representing a pointer to a C primitive
@@ -818,6 +842,7 @@ public class JavaType {
this.structName = null;
this.elementType = null;
this.opaqued = false;
+ this.pascalString = false;
}
/** Constructs a type representing an array of C pointers. */
@@ -831,16 +856,18 @@ public class JavaType {
this.structName = null;
this.elementType = elementType;
this.opaqued = false;
+ this.pascalString = false;
}
/** clone only */
- private JavaType(final C_PTR primitivePointerType, final Class<?> clazz, final String clazzName, final String structName, final Type elementType) {
+ private JavaType(final C_PTR primitivePointerType, final Class<?> clazz, final String clazzName, final String structName, final Type elementType, final boolean pascalString) {
this.primitivePointerType = primitivePointerType;
this.clazz = clazz;
this.clazzName = clazzName;
this.structName = structName;
this.elementType = elementType;
this.opaqued = false;
+ this.pascalString = pascalString;
}
private static String arrayName(Class<?> clazz) {
diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg
index 83045cb..08194e6 100644
--- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg
+++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg
@@ -76,7 +76,7 @@ JavaCallbackDef MessageCallback01 T2_CallbackFunc01 2
# typedef void ( * ALEVENTPROCSOFT)(int eventType, int object, int param, int length, const char *message, void *userParam);
# void alEventCallback(ALEVENTPROCSOFT callback, void *userParam /* identity-key */);
# void alEventCallbackInject(int eventType, int object, int param, const char* msg);
-ArgumentIsString ALEVENTPROCSOFT 4
+ArgumentIsPascalString ALEVENTPROCSOFT 3 4
ArgumentIsString alEventCallbackInject 3
# Define a JavaCallback (OpenAL AL_SOFT_events)