From 0666c8f9a353b42ffde4902dc0cdec66f925a6e3 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 2 Dec 2023 18:32:34 +0100 Subject: Bug 1481 - Hide [ArgumentIs]PascalString argument's length Native pascal strings shall be just treated as normal Java strings on the Java side. Hence drop the length parameter across generated API, i.e. - C Function bindings - Java Callbacks --- doc/manual/index.html | 10 +- doc/manual/index.md | 7 +- make/scripts/runtest.sh | 4 +- .../com/jogamp/gluegen/CMethodBindingEmitter.java | 57 ++++++++-- .../com/jogamp/gluegen/JavaCallbackEmitter.java | 38 ++++--- src/java/com/jogamp/gluegen/JavaConfiguration.java | 51 +-------- src/java/com/jogamp/gluegen/JavaEmitter.java | 83 ++++++++------- .../jogamp/gluegen/JavaMethodBindingEmitter.java | 4 +- src/java/com/jogamp/gluegen/JavaType.java | 116 ++++++++++++++++----- src/java/com/jogamp/gluegen/MethodBinding.java | 4 +- .../gluegen/test/junit/generation/BaseClass.java | 21 ++-- .../junit/generation/BaseClass4JavaCallback.java | 8 +- .../test/junit/generation/Test1p1JavaEmitter.java | 13 ++- .../generation/Test1p2DynamicLibraryBundle.java | 13 ++- .../generation/Test1p2ProcAddressEmitter.java | 4 +- .../gluegen/test/junit/generation/test1-common.cfg | 3 + .../jogamp/gluegen/test/junit/generation/test1.c | 11 ++ .../jogamp/gluegen/test/junit/generation/test1.h | 3 + .../generation/test2-CustomJavaImplCode.java.stub | 4 +- 19 files changed, 278 insertions(+), 176 deletions(-) diff --git a/doc/manual/index.html b/doc/manual/index.html index 68173ec..d9e4b02 100755 --- a/doc/manual/index.html +++ b/doc/manual/index.html @@ -1078,9 +1078,8 @@ using CustomJavaCode to write the exposed API. In this case is most useful in conjunction with RenameJavaMethod.

ArgumentIsString
-Syntax: -ArgumentIsString [function name] [indices...] -where the first argument index is 0
+Syntax: ArgumentIsString [function name] [indices...] where +the first argument index is 0
(optional) For a C function with one or more outgoing char* (or compatible data type) arguments, indicates that those arguments are semantically null-terminated C strings rather than arbitrary arrays of @@ -1089,7 +1088,7 @@ as java.lang.String objects rather than byte[] or ByteBuffer.

ArgumentIsPascalString
Syntax: -ArgumentIsPascalString [function name] [indice-tuples...], +ArgumentIsPascalString [function name] [indice-tuples...], with each tuple being the argument-index for the 'int length' and the 'char* value' argument with index 0 for the the first argument
@@ -1099,7 +1098,8 @@ 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 -byte[] or ByteBuffer.

+byte[] or ByteBuffer as well as dropping the +redundant 'int length' argument on the Java side.

ClassJavadoc
Syntax: ClassJavadoc [class name] [code...]
(optional) Causes the specified line of code to be emitted in the diff --git a/doc/manual/index.md b/doc/manual/index.md index 0ad4172..48a632d 100644 --- a/doc/manual/index.md +++ b/doc/manual/index.md @@ -648,7 +648,7 @@ this case is most useful in conjunction with **ArgumentIsString** Syntax: -`ArgumentIsString [function name] [indices...]` +`ArgumentIsString [function name] [indices...]` where the first argument index is 0 (optional) For a C function with one or more outgoing `char*` (or compatible data type) arguments, indicates that those arguments are @@ -658,7 +658,7 @@ as java.lang.String objects rather than `byte[]` or `ByteBuffer`. **ArgumentIsPascalString** Syntax: -`ArgumentIsPascalString [function name] [indice-tuples...]`, +`ArgumentIsPascalString [function name] [indice-tuples...]`, with each tuple being the argument-index for the '`int length`' and the '`char* value`' argument with index 0 for the the first argument (optional) For a C function with one or more outgoing '`int length`' and @@ -666,7 +666,8 @@ with each tuple being the argument-index for the '`int length`' and the 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 `byte[]` or `ByteBuffer`. +java.lang.String objects rather than `byte[]` or `ByteBuffer` +as well as dropping the redundant '`int length`' argument on the Java side. **ClassJavadoc** Syntax: `ClassJavadoc [class name] [code...]` diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh index 570c5f1..a965868 100755 --- a/make/scripts/runtest.sh +++ b/make/scripts/runtest.sh @@ -99,7 +99,7 @@ function onetest() { echo } # -onetest com.jogamp.common.GlueGenVersion 2>&1 | tee -a $LOG +#onetest com.jogamp.common.GlueGenVersion 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestSystemPropsAndEnvs 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestVersionInfo 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestVersionNumber 2>&1 | tee -a $LOG @@ -168,7 +168,7 @@ onetest com.jogamp.common.GlueGenVersion 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.jcpp.VaArgsPastingTest 2>&1 | tee -a $LOG -#onetest com.jogamp.gluegen.test.junit.generation.Test1p1JavaEmitter 2>&1 | tee -a $LOG +onetest com.jogamp.gluegen.test.junit.generation.Test1p1JavaEmitter 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p2ProcAddressEmitter 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p2LoadJNIAndImplLib 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p2DynamicLibraryBundle 2>&1 | tee -a $LOG diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index cbd33fd..9d0eb96 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -384,7 +384,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { assert(binding.getNumArguments() == 1); continue; } - if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + if (javaArgType.isJNIEnv() || javaArgType.isPascalLen() || binding.isArgumentThisPointer(i)) { continue; } buf.append(", "); @@ -730,11 +730,16 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(", _copyIndex);"); if (javaArgType.isStringArray()) { + if ( javaArgType.isPascalStr() ) { + throw new GlueGenException( + "Cannot handle String[] of type PascalString java " + javaArgType.getDebugString() + ", c "+ cArgType.getDebugString() + + "\": "+binding, binding.getCSymbol().getASTLocusTag()); + } unit.emit(" "); emitGetStringChars("(jstring) _tmpObj", convName+"_copy[_copyIndex]", isUTF8Type(cArgType), - true); + false, true); } else if (javaArgType.isNIOBufferArray()) { /* We always assume an integer "byte offset" argument follows any Buffer in the method binding. */ @@ -791,10 +796,13 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(" }"); } else if (javaArgType.isString()) { + if ( javaArgType.isPascalStr() ) { + unit.emitln(" size_t "+STRING_CHARS_PREFIX + javaArgName + "_len;"); + } emitGetStringChars(javaArgName, STRING_CHARS_PREFIX + javaArgName, isUTF8Type(binding.getCArgumentType(i)), - false); + javaArgType.isPascalStr(), false); } } } @@ -953,6 +961,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { if (javaArgType.isJNIEnv()) { unit.emit("env"); + } else if( javaArgType.isPascalLen() ) { + final Type cArgType = binding.getCArgumentType(i); + unit.emit(" ("+cArgType.getCName(true)+") "+STRING_CHARS_PREFIX + binding.getArgumentName(javaArgType.pascalStrElem.valueIdx) + "_len"); } else if (binding.isArgumentThisPointer(i)) { unit.emit(CMethodBindingEmitter.cThisArgumentName()); } else { @@ -1182,11 +1193,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { } unit.emitln(" }"); } else if (javaType.isString()) { - final boolean pascalString = javaType.isPascalStringVariant(); + final boolean pascalString = javaType.isPascalStrElem(); final String lenArgName; if( pascalString ) { - final int lenIdx = cfg.pascalStringLengthIndex(getCSymbol(), argIdx); - lenArgName = 0 <= lenIdx ? binding.getArgumentName(lenIdx) : null; + lenArgName = binding.getArgumentName( javaType.pascalStrElem.lengthIdx ); } else { lenArgName = null; } @@ -1315,7 +1325,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { throw new GlueGenException("Saw illegal \"void\" argument while emitting arg "+i+" of "+binding, binding.getCArgumentType(i).getASTLocusTag()); } - } else { + } else if ( type.isPascalLen() ) { + // drop + } else { Class c = type.getJavaClass(); if (c != null) { JavaType.appendJNIDescriptor(buf, c, false); @@ -1411,10 +1423,19 @@ public class CMethodBindingEmitter extends FunctionEmitter { private void emitGetStringChars(final String sourceVarName, final String receivingVarName, final boolean isUTF8, + final boolean addLengthVar, final boolean emitElseClause) { unit.emitln(" if ( NULL != " + sourceVarName + " ) {"); + if( addLengthVar ) { + unit.emit(" "+receivingVarName+"_len = (size_t) "); + } if (isUTF8) { + if( addLengthVar ) { + unit.emit(" (*env)->GetStringUTFLength(env, "); + unit.emit(sourceVarName); + unit.emitln(");"); + } unit.emit(" "); unit.emit(receivingVarName); unit.emit(" = (*env)->GetStringUTFChars(env, "); @@ -1431,12 +1452,26 @@ public class CMethodBindingEmitter extends FunctionEmitter { // null-terminated Unicode string. For this reason we explicitly // calloc our buffer, including the null terminator, and use // GetStringRegion to fetch the string's characters. - emitCalloc(receivingVarName, - "jchar", - "(*env)->GetStringLength(env, " + sourceVarName + ") + 1", - "Could not allocate temporary buffer for copying string argument \\\""+sourceVarName+"\\\""); + if( addLengthVar ) { + unit.emit(" (*env)->GetStringLength(env, "); + unit.emit(sourceVarName); + unit.emitln(");"); + emitCalloc(receivingVarName, + "jchar", + receivingVarName+"_len + 1", + "Could not allocate temporary buffer for copying string argument \\\""+sourceVarName+"\\\""); + } else { + emitCalloc(receivingVarName, + "jchar", + "(*env)->GetStringLength(env, " + sourceVarName + ") + 1", + "Could not allocate temporary buffer for copying string argument \\\""+sourceVarName+"\\\""); + } unit.emitln(" (*env)->GetStringRegion(env, " + sourceVarName + ", 0, (*env)->GetStringLength(env, " + sourceVarName + "), " + receivingVarName + ");"); } + if( addLengthVar ) { + unit.emitln(" } else {"); + unit.emitln(" "+receivingVarName+"_len = 0;"); + } unit.emit(" }"); if (emitElseClause) { unit.emit(" else {"); diff --git a/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java b/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java index 3359c1c..1c6a3c7 100644 --- a/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaCallbackEmitter.java @@ -483,7 +483,7 @@ public final class JavaCallbackEmitter { final StringBuilder buf = new StringBuilder(); buf.append("("); info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() ) { + if( !cType.isVoid() && !jType.isPascalLen() ) { if( idx == info.cbFuncUserParamIdx ) { buf.append("J"); } else { @@ -568,7 +568,7 @@ public final class JavaCallbackEmitter { final boolean[] mapNativePtrToCompound = { false }; final JavaType[] origUserParamJType = { null }; info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() ) { + if( !cType.isVoid() && !jType.isPascalLen() ) { if( 0 < consumedCount ) { unit.emit(", "); } if( idx == info.cbFuncUserParamIdx ) { unit.emit("long nativeUserParam"); @@ -623,10 +623,12 @@ public final class JavaCallbackEmitter { } unit.emit("value.func.callback("); info.cbFuncBinding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() ) { + if( !cType.isVoid() && !jType.isPascalLen() ) { if( 0 < consumedCount ) { unit.emit(", "); } if( idx == info.cbFuncUserParamIdx && !useParamLocal[0] ) { unit.emit("value.param"); + } else if( jType.isPascalLen() ){ + unit.emit( "/* "+name+" */"); } else { unit.emit(name); } @@ -834,7 +836,7 @@ public final class JavaCallbackEmitter { } unit.emit("(*env)->CallStatic" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cbClazz, cbMethod, "); // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); - emitJavaCallbackBodyPassJavaArguments(unit); + emitCBodyPassArguments(unit); unit.emitln(");"); unit.emitln(" if( (*env)->ExceptionCheck(env) ) {"); unit.emitln(" fprintf(stderr, \"Info: Callback '"+jcbFriendlyBasename+"': Exception in Java Callback caught:\\n\");"); @@ -865,24 +867,26 @@ public final class JavaCallbackEmitter { return count; } - private int emitJavaCallbackBodyPassJavaArguments(final CodeUnit unit) { + private int emitCBodyPassArguments(final CodeUnit unit) { int count = 0; boolean needsComma = false; for (int i = 0; i < info.cbFuncBinding.getNumArguments(); i++) { - if (needsComma) { - unit.emit(", "); - needsComma = false; - } - final String baseArgName = info.cbFuncBinding.getArgumentName(i); final JavaType currentJavaType = info.cbFuncBinding.getJavaArgumentType(i); - if( i != info.cbFuncUserParamIdx && currentJavaType.isCompoundTypeWrapper() ) { - final String cBaseArgName = CodeGenUtils.capitalizeString( baseArgName ); - unit.emit( "(*env)->CallStaticObjectMethod(env, cbClazzArg" + cBaseArgName + ", cbMethodArg" + cBaseArgName + ", " + baseArgName + "_jni)" ); - } else { - unit.emit( baseArgName + "_jni" ); + if( !currentJavaType.isPascalLen() ) { + if (needsComma) { + unit.emit(", "); + needsComma = false; + } + final String baseArgName = info.cbFuncBinding.getArgumentName(i); + if( i != info.cbFuncUserParamIdx && currentJavaType.isCompoundTypeWrapper() ) { + final String cBaseArgName = CodeGenUtils.capitalizeString( baseArgName ); + unit.emit( "(*env)->CallStaticObjectMethod(env, cbClazzArg" + cBaseArgName + ", cbMethodArg" + cBaseArgName + ", " + baseArgName + "_jni)" ); + } else { + unit.emit( baseArgName + "_jni" ); + } + needsComma = true; + ++count; } - needsComma = true; - ++count; } return count; } diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index bc78b48..f247ebf 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -144,35 +144,6 @@ public class JavaConfiguration { private final Map returnedArrayLengths = new HashMap(); private final Set maxOneElement = new HashSet(); - /** 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 indices) { - indices.add(valueIndex); - } - public static final List pushValueIndex(final List source, List indices) { - if( null == indices ) { - indices = new ArrayList(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 @@ -183,7 +154,7 @@ public class JavaConfiguration { * 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> argumentsArePascalString = new HashMap>(); + private final Map> argumentsArePascalString = new HashMap>(); /** JavaCallback configuration definition (static) */ public static class JavaCallbackDef { @@ -668,11 +639,11 @@ public class JavaConfiguration { /** Returns a list of PascalStringIdx which are tuples of indices of int len, const char* arguments that should be converted to Strings. Returns null if there are no such hints for the given function alias symbol. */ - public List pascalStringArgument(final AliasedSymbol symbol) { + public List pascalStringArgument(final AliasedSymbol symbol) { final String name = symbol.getName(); final Set aliases = symbol.getAliasedNames(); - List res = argumentsArePascalString.get(name); + List res = argumentsArePascalString.get(name); if( null == res ) { res = oneInMap(argumentsArePascalString, aliases); if( null == res ) { @@ -683,18 +654,6 @@ public class JavaConfiguration { return res; } - public int pascalStringLengthIndex(final AliasedSymbol symbol, final int valueIndex) { - final List 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 ) { @@ -2058,11 +2017,11 @@ public class JavaConfiguration { protected void readArgumentIsPascalString(final StringTokenizer tok, final String filename, final int lineNo) { try { final String methodName = tok.nextToken(); - final List pascalTuples = new ArrayList(2); + final List pascalTuples = new ArrayList(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)); + pascalTuples.add(new JavaType.PascalStringElem(lenIdx, valIdx)); } if( pascalTuples.size() > 0 ) { argumentsArePascalString.put(methodName, pascalTuples); diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index 0d26071..1725b18 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -79,7 +79,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.JavaType.PascalStringElem; import com.jogamp.gluegen.Logging.LoggerIf; import com.jogamp.gluegen.cgram.types.AliasedSymbol; import com.jogamp.gluegen.cgram.types.ArrayType; @@ -309,7 +309,7 @@ public class JavaEmitter implements GlueEmitter { } } - /** Mangle a class, package or function name for JNI usage, i.e. replace all '.' w/ '_' */ + /** Mangle a class, package or function name for JNI usage, i.e. replace all '_' w/ '_1' and '.' w/ '_' */ protected static String jniMangle(final String name) { return name.replaceAll("_", "_1").replace('.', '_'); } @@ -3105,8 +3105,8 @@ 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); + private JavaType javaStringType(final Class c, final PascalStringElem pascalStrElem) { + return JavaType.createForStringClass(c, pascalStrElem); } /** Maps the C types in the specified function to Java types through @@ -3140,7 +3140,7 @@ public class JavaEmitter implements GlueEmitter { "\". ReturnsString requires native method to have return type \"char *\"", sym.getASTLocusTag()); } - javaReturnType = javaStringType(java.lang.String.class, false); + javaReturnType = javaStringType(java.lang.String.class, null); } else { final JavaType r = cfg.getOpaqueReturnType(sym); if( null != r ) { @@ -3153,10 +3153,10 @@ 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 javaArgumentTypes = new ArrayList(); - List stringArgIndices = cfg.stringArguments(sym); - final List pascalStringArgs = cfg.pascalStringArgument(sym); + List stringValueIndices = cfg.stringArguments(sym); + final List pascalStringArgs = cfg.pascalStringArgument(sym); if( null != pascalStringArgs ) { - stringArgIndices = PascalStringIdx.pushValueIndex(pascalStringArgs, stringArgIndices); + stringValueIndices = JavaType.PascalStringElem.pushValueIndex(pascalStringArgs, stringValueIndices); } final JavaCallbackInfo jcbi = cfg.setFuncToJavaCallbackMap.get( sym.getName() ); int jcbiSetFuncCBParamIdx=-1; @@ -3191,36 +3191,45 @@ public class JavaEmitter implements GlueEmitter { // fallthrough intended } } - if ( 0 == mapMode && 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() || - mappedType.isCCharPointerType() || - mappedType.isCShortPointerType() || - mappedType.isNIOPointerBuffer() || - ( mappedType.isArray() && - ( mappedType.getJavaClass() == ArrayTypes.byteBufferArrayClass ) || - ( mappedType.getJavaClass() == ArrayTypes.shortBufferArrayClass ) ) ) - { - // 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 = javaStringType(ArrayTypes.stringArrayClass, pascalString); - mapMode = 30; - } else { - mappedType = javaStringType(String.class, pascalString); - mapMode = 31; + if( 0 == mapMode ) { + if ( stringValueIndices != null && stringValueIndices.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() || + mappedType.isCCharPointerType() || + mappedType.isCShortPointerType() || + mappedType.isNIOPointerBuffer() || + ( mappedType.isArray() && + ( mappedType.getJavaClass() == ArrayTypes.byteBufferArrayClass ) || + ( mappedType.getJavaClass() == ArrayTypes.shortBufferArrayClass ) ) ) + { + // convert mapped type from: + // void*, byte[], and short[] to String + // ByteBuffer[] and ShortBuffer[] to String[] + final JavaType.PascalStringElem pascalStrElem = JavaType.PascalStringElem.getByValueIdx(pascalStringArgs, i); + if (mappedType.isArray() || mappedType.isNIOPointerBuffer()) { + mappedType = javaStringType(ArrayTypes.stringArrayClass, pascalStrElem); + mapMode = 30; + } else { + mappedType = javaStringType(String.class, pascalStrElem); + mapMode = 31; + } + } else { + mapMode = 99; + throw new GlueGenException( + "Cannot apply ArgumentIsString configuration directive to " + + "argument " + i + " of \"" + sym + "\": argument type is not " + + "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent", + sym.getASTLocusTag()); + } + } else if ( ( mappedType.isInt() || mappedType.isLong() ) && null != pascalStringArgs ) { + final JavaType.PascalStringElem pascalStrElem = JavaType.PascalStringElem.getByLengthIdx(pascalStringArgs, i); + if( null != pascalStrElem ) { + // mark pascal-string length in Java API to be erased for using Java String length + mapMode = 40; + mappedType = new JavaType(mappedType, pascalStrElem); + } } - } else { - mapMode = 99; - throw new GlueGenException( - "Cannot apply ArgumentIsString configuration directive to " + - "argument " + i + " of \"" + sym + "\": argument type is not " + - "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent", - sym.getASTLocusTag()); - } } javaArgumentTypes.add(mappedType); LOG.log(INFO, "BindFunc: {0}: added mapping ({1}) for {2} from C type: {3} to Java type: {4}", diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 1b510c0..ee2adc9 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -385,7 +385,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { continue; } - if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + if ( type.isJNIEnv() || type.isPascalLen() || binding.isArgumentThisPointer(i) ) { // Don't need to expose these at the Java level continue; } @@ -661,7 +661,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } for (int i = 0; i < binding.getNumArguments(); i++) { final JavaType type = binding.getJavaArgumentType(i); - if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + if (type.isJNIEnv() || type.isPascalLen() || binding.isArgumentThisPointer(i)) { // Don't need to expose these at the Java level continue; } diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java index d58eca5..7e894c8 100644 --- a/src/java/com/jogamp/gluegen/JavaType.java +++ b/src/java/com/jogamp/gluegen/JavaType.java @@ -41,6 +41,8 @@ package com.jogamp.gluegen; import java.nio.*; +import java.util.ArrayList; +import java.util.List; import com.jogamp.gluegen.cgram.types.*; @@ -59,13 +61,67 @@ public class JavaType { VOID, CHAR, SHORT, INT32, INT64, FLOAT, DOUBLE; } - private final Class clazz; // Primitive types and other types representable as Class objects + /** Pascal string argument index tuple for length and value. */ +public static class PascalStringElem { + public final int lengthIdx; + public final int valueIdx; + + PascalStringElem(final int lenIdx, final int valIdx) { + lengthIdx = lenIdx; + valueIdx = valIdx; + } + + public static final List pushValueIndex(final List source, List indices) { + if( null == indices ) { + indices = new ArrayList(2); + } + for(final PascalStringElem p : source) { + indices.add(p.valueIdx); + } + return indices; + } + public static final List pushLengthIndex(final List source) { + final List lengths = new ArrayList(2); + for(final PascalStringElem p : source) { + lengths.add(p.lengthIdx); + } + return lengths; + } + + public static PascalStringElem getByValueIdx(final List pascals, final int valueIdx) { + if( null != pascals ) { + for(final PascalStringElem p : pascals) { + if( valueIdx == p.valueIdx ) { + return p; + } + } + } + return null; + } + public static PascalStringElem getByLengthIdx(final List pascals, final int lengthIdx) { + if( null != pascals ) { + for(final PascalStringElem p : pascals) { + if( lengthIdx == p.lengthIdx ) { + return p; + } + } + } + return null; + } + + @Override + public String toString() { + return "PascalStr[lenIdx "+lengthIdx+", valIdx "+valueIdx+"]"; + } +} + +private final Class clazz; // Primitive types and other types representable as Class objects private final String clazzName; // Future (not yet generated or existing) Class objects (existing at runtime) private final String structName; // Types we're generating glue code for (i.e., C structs) 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; + public final PascalStringElem pascalStrElem; private static JavaType objectType; private static JavaType nioBufferType; @@ -131,7 +187,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, false); + return new JavaType(clazz, true, null); } /** Creates a JavaType corresponding to the given Java type. This @@ -139,11 +195,10 @@ 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, false); + return new JavaType(clazz, false, null); } - - public static JavaType createForStringClass(final Class clazz, final boolean pascalString) { - return new JavaType(clazz, false, pascalString); + public static JavaType createForStringClass(final Class clazz, final PascalStringElem pascalStrElem) { + return new JavaType(clazz, false, pascalStrElem); } /** @@ -567,11 +622,11 @@ public class JavaType { return (clazz == java.lang.String.class); } - public boolean isPascalStringVariant() { return pascalString; } + public boolean isPascalStrElem() { return null != pascalStrElem; } - public boolean isPascalString() { - return isString() && this.pascalString; - } + public boolean isPascalStr() { return isPascalStrElem() && isString() ; } + + public boolean isPascalLen() { return isPascalStrElem() && !isString() ; } public boolean isArray() { return ((clazz != null) && clazz.isArray()); @@ -605,9 +660,7 @@ public class JavaType { return (clazz != null && clazz.isArray() && clazz.getComponentType() == java.lang.String.class); } - public boolean isPascalStringArray() { - return isStringArray() && this.pascalString; - } + public boolean isPascalStrArray() { return isPascalStrElem() && isStringArray() ; } public boolean isPrimitive() { return ((clazz != null) && !isArray() && clazz.isPrimitive() && (clazz != Void.TYPE)); @@ -696,7 +749,7 @@ public class JavaType { @Override public Object clone() { - return new JavaType(primitivePointerType, clazz, clazzName, structName, elementType, pascalString); + return new JavaType(primitivePointerType, clazz, clazzName, structName, elementType, pascalStrElem); } @Override @@ -747,16 +800,13 @@ public class JavaType { if( isOpaqued() ) { append(sb, "opaque", prepComma); prepComma=true; } + if( isPascalStrElem() ) { + sb.append("pascal "); + } 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; @@ -800,7 +850,7 @@ public class JavaType { * Constructs a representation for a type corresponding to the given Class * argument. */ - private JavaType(final Class clazz, final boolean opaqued, final boolean pascalString) { + private JavaType(final Class clazz, final boolean opaqued, final PascalStringElem pascalStrElem) { if( null == clazz ) { throw new IllegalArgumentException("null clazz passed"); } @@ -810,7 +860,7 @@ public class JavaType { this.structName = null; this.elementType = null; this.opaqued = opaqued; - this.pascalString = pascalString; + this.pascalStrElem = pascalStrElem; } /** Constructs a type representing a either a named clazz or a named C struct.*/ @@ -831,7 +881,7 @@ public class JavaType { this.clazz = null; this.elementType = null; this.opaqued = false; - this.pascalString = false; + this.pascalStrElem = null; } /** Constructs a type representing a pointer to a C primitive @@ -846,7 +896,7 @@ public class JavaType { this.structName = null; this.elementType = null; this.opaqued = false; - this.pascalString = false; + this.pascalStrElem = null; } /** Constructs a type representing an array of C pointers. */ @@ -860,18 +910,28 @@ public class JavaType { this.structName = null; this.elementType = elementType; this.opaqued = false; - this.pascalString = false; + this.pascalStrElem = null; } /** clone only */ - private JavaType(final C_PTR primitivePointerType, final Class clazz, final String clazzName, final String structName, final Type elementType, final boolean pascalString) { + private JavaType(final C_PTR primitivePointerType, final Class clazz, final String clazzName, final String structName, final Type elementType, + final PascalStringElem pascalStrElem) + { this.primitivePointerType = primitivePointerType; this.clazz = clazz; this.clazzName = clazzName; this.structName = structName; this.elementType = elementType; this.opaqued = false; - this.pascalString = pascalString; + this.pascalStrElem = pascalStrElem; + } + /** Copy ctor */ + public JavaType(final JavaType o) { + this(o.primitivePointerType, o.clazz, o.clazzName, o.structName, o.elementType, o.pascalStrElem); + } + /** Copy ctor w/ pascalString variant override */ + public JavaType(final JavaType o, final PascalStringElem pascalStrElem) { + this(o.primitivePointerType, o.clazz, o.clazzName, o.structName, o.elementType, pascalStrElem); } private static String arrayName(Class clazz) { diff --git a/src/java/com/jogamp/gluegen/MethodBinding.java b/src/java/com/jogamp/gluegen/MethodBinding.java index 6059003..cb9e6ed 100644 --- a/src/java/com/jogamp/gluegen/MethodBinding.java +++ b/src/java/com/jogamp/gluegen/MethodBinding.java @@ -221,7 +221,7 @@ public class MethodBinding { */ public StringBuilder getJavaParameterList(final StringBuilder buf, final List exclude) { forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { - if( !cType.isVoid() && ( null == exclude || !exclude.contains(idx) ) ) { + if( !cType.isVoid() && !jType.isPascalLen() && ( null == exclude || !exclude.contains(idx) ) ) { if( 0 < consumedCount ) { buf.append(", "); } @@ -731,7 +731,7 @@ public class MethodBinding { assert(getNumArguments() == 1); continue; } - if (type.isJNIEnv() || isArgumentThisPointer(i)) { + if (type.isJNIEnv() || type.isPascalLen() || isArgumentThisPointer(i)) { // Don't need to expose these at the Java level continue; } diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java index dc49475..a236e85 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -48,10 +48,6 @@ import jogamp.common.os.MachineDataInfoRuntime; import org.junit.Assert; -/** - * @author Michael Bien - * @author Sven Gothel - */ public class BaseClass extends SingletonJunitCase { static { @@ -214,6 +210,8 @@ public class BaseClass extends SingletonJunitCase { l = binding.typeTestPtrDiffT(l, l); l = binding.typeTestIntPtrT(l, l); l = binding.typeTestUIntPtrT(l, l); + + i = binding.addStrlenAndPascalStrLen("lala"); } /** @@ -2523,7 +2521,7 @@ public class BaseClass extends SingletonJunitCase { binding.destroyTKFieldImmutable(model); } - public void chapter15TestTKMixed(final Bindingtest1 binding) throws Exception { + public void chapter14TestTKMixed(final Bindingtest1 binding) throws Exception { Assert.assertEquals(false, TK_ModelMixed.usesNativeCode()); final TK_ModelMixed model = binding.createTKModelMixed(); @@ -2568,7 +2566,16 @@ public class BaseClass extends SingletonJunitCase { assertAPTR(surfaceContext, model.getCtx()); } - public void chapter14TestCustomJNICode(final Bindingtest1 binding) throws Exception { + public void chapter15TestCustomJNICode(final Bindingtest1 binding) throws Exception { Assert.assertEquals(Bindingtest1.FOO_VALUE, binding.getFoo()); } + + public void chapter16TestPascalString(final Bindingtest1 binding) throws Exception { + Assert.assertEquals(false, TK_ModelMixed.usesNativeCode()); + Assert.assertEquals( 2, binding.addStrlenAndPascalStrLen("1")); + Assert.assertEquals( 8, binding.addStrlenAndPascalStrLen("1234")); + Assert.assertEquals(10, binding.addStrlenAndPascalStrLen("12345")); + Assert.assertEquals( 0, binding.addStrlenAndPascalStrLen(null)); + } + } diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass4JavaCallback.java b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass4JavaCallback.java index cab8a9c..0838e72 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass4JavaCallback.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/BaseClass4JavaCallback.java @@ -898,7 +898,7 @@ public class BaseClass4JavaCallback extends BaseClass { final String[] msg_res = { null }; final ALEVENTPROCSOFT myCallback01 = new ALEVENTPROCSOFT() { @Override - public void callback(final int eventType, final int object, final int param, final int length, final String message, final ALCcontext context) { + public void callback(final int eventType, final int object, final int param, final String message, final ALCcontext context) { id_res[0] = object; msg_res[0] = message; System.err.println("chapter05a.myCallback01: type "+eventType+", obj "+object+", param "+param+", '"+message+"', userParam 0x"+ @@ -907,7 +907,7 @@ public class BaseClass4JavaCallback extends BaseClass { }; final ALEVENTPROCSOFT myCallback02 = new ALEVENTPROCSOFT() { @Override - public void callback(final int eventType, final int object, final int param, final int length, final String message, final ALCcontext context) { + public void callback(final int eventType, final int object, final int param, final String message, final ALCcontext context) { id_res[0] = 1000 * object; msg_res[0] = message; System.err.println("chapter05a.myCallback02: type "+eventType+", obj "+object+", param "+param+", '"+message+"', userParam 0x"+ @@ -1018,7 +1018,7 @@ public class BaseClass4JavaCallback extends BaseClass { final String[] msg_res = { null }; final ALEVENTPROCSOFT myCallback01 = new ALEVENTPROCSOFT() { @Override - public void callback(final int eventType, final int object, final int param, final int length, final String message, final ALCcontext context) { + public void callback(final int eventType, final int object, final int param, final String message, final ALCcontext context) { id_res[0] = object; msg_res[0] = message; System.err.println("chapter05.myCallback01: type "+eventType+", obj "+object+", param "+param+", '"+message+"', userParam 0x"+ @@ -1027,7 +1027,7 @@ public class BaseClass4JavaCallback extends BaseClass { }; final ALEVENTPROCSOFT myCallback02 = new ALEVENTPROCSOFT() { @Override - public void callback(final int eventType, final int object, final int param, final int length, final String message, final ALCcontext context) { + public void callback(final int eventType, final int object, final int param, final String message, final ALCcontext context) { id_res[0] = 1000 * object; msg_res[0] = message; System.err.println("chapter05.myCallback02: type "+eventType+", obj "+object+", param "+param+", '"+message+"', userParam 0x"+ diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java index c7c5644..f92aef3 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p1JavaEmitter.java @@ -161,16 +161,21 @@ public class Test1p1JavaEmitter extends BaseClass { } @Test - public void chapter15TestTKMixed() throws Exception { - chapter15TestTKMixed(new Bindingtest1p1Impl()); + public void chapter14TestTKMixed() throws Exception { + chapter14TestTKMixed(new Bindingtest1p1Impl()); } /** * Test Custom JNI Code invocation */ @Test - public void chapter14TestCustomJNICode() throws Exception { - chapter14TestCustomJNICode(new Bindingtest1p1Impl()); + public void chapter15TestCustomJNICode() throws Exception { + chapter15TestCustomJNICode(new Bindingtest1p1Impl()); + } + + @Test + public void chapter16TestPascalString() throws Exception { + chapter16TestPascalString(new Bindingtest1p1Impl()); } public static void main(final String args[]) throws IOException { diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2DynamicLibraryBundle.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2DynamicLibraryBundle.java index fbf0ff0..4e5b268 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2DynamicLibraryBundle.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2DynamicLibraryBundle.java @@ -171,16 +171,21 @@ public class Test1p2DynamicLibraryBundle extends BaseClass { } @Test - public void chapter15TestTKMixed() throws Exception { - chapter15TestTKMixed(new Bindingtest1p2Impl()); + public void chapter14TestTKMixed() throws Exception { + chapter14TestTKMixed(new Bindingtest1p2Impl()); } /** * Test Custom JNI Code invocation */ @Test - public void chapter14TestCustomJNICode() throws Exception { - chapter14TestCustomJNICode(new Bindingtest1p2Impl()); + public void chapter15TestCustomJNICode() throws Exception { + chapter15TestCustomJNICode(new Bindingtest1p2Impl()); + } + + @Test + public void chapter16TestPascalString() throws Exception { + chapter16TestPascalString(new Bindingtest1p2Impl()); } /** diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java index b2c1974..22db353 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test1p2ProcAddressEmitter.java @@ -172,7 +172,7 @@ public class Test1p2ProcAddressEmitter extends BaseClass { @Test public void chapter15TestTKMixed() throws Exception { - chapter15TestTKMixed(new Bindingtest1p2Impl()); + chapter14TestTKMixed(new Bindingtest1p2Impl()); } /** @@ -180,7 +180,7 @@ public class Test1p2ProcAddressEmitter extends BaseClass { */ @Test public void chapter14TestCustomJNICode() throws Exception { - chapter14TestCustomJNICode(new Bindingtest1p2Impl()); + chapter15TestCustomJNICode(new Bindingtest1p2Impl()); } /** diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg index 05bed70..7111f77 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1-common.cfg @@ -369,6 +369,9 @@ ReturnValueCapacity createTKStructImmutable sizeof(TK_StructImmutable) ReturnValueCapacity createTKField sizeof(TK_Field) ReturnValueCapacity createTKModelMixed sizeof(TK_ModelMixed) +# MYAPI int addStrlenAndPascalStrLen(int length, const char* val); +ArgumentIsPascalString addStrlenAndPascalStrLen 0 1 + # Imports needed by all glue code Import java.nio.* Import java.util.* diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c index c5ea388..07763b9 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.c @@ -1056,3 +1056,14 @@ MYAPI void MYAPIENTRY destroyTKModelMixed(TK_ModelMixed * s) { free(s); } +MYAPI int addStrlenAndPascalStrLen(int length, const char* val) { + if( NULL == val ) { + fprintf(stderr, "addStrlenAndPascalStrLen: %d + %d of NULL\n", 0, length); + return length; + } else { + int len2 = (int)strlen(val); + fprintf(stderr, "addStrlenAndPascalStrLen: %d + %d of '%s'\n", len2, length, val); + return len2 + length; + } +} + diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h index 3b74040..021bca1 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test1.h @@ -767,3 +767,6 @@ typedef struct { MYAPI TK_ModelMixed* MYAPIENTRY createTKModelMixed(); MYAPI void MYAPIENTRY destroyTKModelMixed(TK_ModelMixed* s); +// Pascal String test +MYAPI int addStrlenAndPascalStrLen(int length, const char* val); + diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2-CustomJavaImplCode.java.stub b/src/junit/com/jogamp/gluegen/test/junit/generation/test2-CustomJavaImplCode.java.stub index 9d58e02..ab360a6 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2-CustomJavaImplCode.java.stub +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2-CustomJavaImplCode.java.stub @@ -18,7 +18,7 @@ if( null == value ) { return; } - value.func.callback(eventType, object, param, message.length(), message, userParam2); + value.func.callback(eventType, object, param, message, userParam2); } public void alEventCallback1Inject(ALCcontext userParam, int eventType, int object, int param, String message) { @@ -41,6 +41,6 @@ if( null == value ) { return; } - value.func.callback(eventType, object, param, message.length(), message, userParam2); + value.func.callback(eventType, object, param, message, userParam2); } -- cgit v1.2.3