diff options
author | Sven Gothel <[email protected]> | 2023-07-05 10:21:48 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-07-05 10:21:48 +0200 |
commit | 5ceca8550b82591a6a2661a26d3e0d5e6e3e15ff (patch) | |
tree | 95768138afdb5ee4c6d3b705e803ad497241f756 /src | |
parent | ae4c2c3e59ed92caa6f0e18360b7236e50899bf6 (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
Diffstat (limited to 'src')
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 30 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaConfiguration.java | 93 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 19 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaType.java | 45 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg | 2 |
5 files changed, 164 insertions, 25 deletions
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) |