diff options
Diffstat (limited to 'src/net')
16 files changed, 1584 insertions, 2265 deletions
diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java index 2616f151b..4a7ad1c98 100644 --- a/src/net/java/games/gluegen/CMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -57,8 +57,6 @@ public class CMethodBindingEmitter extends FunctionEmitter protected MethodBinding binding; - protected boolean indirectBufferInterface = false; - /** Name of the package in which the corresponding Java method resides.*/ private String packageName; @@ -77,6 +75,10 @@ public class CMethodBindingEmitter extends FunctionEmitter */ private boolean isJavaMethodStatic; + // Flags which change various aspects of glue code generation + protected boolean forImplementingMethodCall; + protected boolean forIndirectBufferAndArrayImplementation; + /** * Optional List of Strings containing temporary C variables to declare. */ @@ -117,11 +119,13 @@ public class CMethodBindingEmitter extends FunctionEmitter * being bound. */ public CMethodBindingEmitter(MethodBinding binding, - boolean isOverloadedBinding, + PrintWriter output, String javaPackageName, String javaClassName, + boolean isOverloadedBinding, boolean isJavaMethodStatic, - PrintWriter output) + boolean forImplementingMethodCall, + boolean forIndirectBufferAndArrayImplementation) { super(output); @@ -134,21 +138,14 @@ public class CMethodBindingEmitter extends FunctionEmitter this.className = javaClassName; this.isOverloadedBinding = isOverloadedBinding; this.isJavaMethodStatic = isJavaMethodStatic; - setCommentEmitter(defaultCommentEmitter); - } - - public final MethodBinding getBinding() { return binding; } + this.forImplementingMethodCall = forImplementingMethodCall; + this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; - public boolean isIndirectBufferInterface() { - return indirectBufferInterface; - } - - - public void setIndirectBufferInterface(boolean indirect) { - indirectBufferInterface = indirect; + setCommentEmitter(defaultCommentEmitter); } + public final MethodBinding getBinding() { return binding; } public String getName() { return binding.getName(); @@ -164,8 +161,8 @@ public class CMethodBindingEmitter extends FunctionEmitter /** * If this function returns a void* encapsulated in a - * java.nio.Buffer, sets the expression for the capacity of the - * returned Buffer. + * java.nio.Buffer (or compound type wrapper), sets the expression + * for the capacity of the returned Buffer. * * @param expression a MessageFormat which, when applied to an array * of type String[] that contains each of the arguments names of the @@ -175,18 +172,20 @@ public class CMethodBindingEmitter extends FunctionEmitter * returned from this method. * * @throws IllegalArgumentException if the <code> - * binding.getJavaReturnType().isNIOBuffer() == false + * binding.getJavaReturnType().isNIOBuffer() == false and + * binding.getJavaReturnType().isCompoundTypeWrapper() == false * </code> */ public final void setReturnValueCapacityExpression(MessageFormat expression) { returnValueCapacityExpression = expression; - if (!binding.getJavaReturnType().isNIOBuffer()) + if (!binding.getJavaReturnType().isNIOBuffer() && + !binding.getJavaReturnType().isCompoundTypeWrapper()) { throw new IllegalArgumentException( "Cannot specify return value capacity for a method that does not " + - "return java.nio.Buffer: \"" + binding + "\""); + "return java.nio.Buffer or a compound type wrapper: \"" + binding + "\""); } } @@ -217,7 +216,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { returnValueLengthExpression = expression; - if (!binding.getJavaReturnType().isArray()) + if (!binding.getJavaReturnType().isArray() && + !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers()) { throw new IllegalArgumentException( "Cannot specify return value length for a method that does not " + @@ -285,6 +285,12 @@ public class CMethodBindingEmitter extends FunctionEmitter */ public final boolean getIsJavaMethodStatic() { return isJavaMethodStatic; } + /** + * Is this CMethodBindingEmitter implementing the case of an + * indirect buffer or array being passed down to C code? + */ + public final boolean forIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } + protected void emitReturnType(PrintWriter writer) { writer.print("JNIEXPORT "); @@ -314,10 +320,19 @@ public class CMethodBindingEmitter extends FunctionEmitter } } + protected String getImplSuffix() { + if (forImplementingMethodCall) { + if (forIndirectBufferAndArrayImplementation) { + return "1"; + } else { + return "0"; + } + } + return ""; + } + protected int emitArguments(PrintWriter writer) { - int numBufferOffsetArgs = 0, numBufferOffsetArrayArgs = 0; - writer.print("JNIEnv *env, "); int numEmitted = 1; // initially just the JNIEnv if (isJavaMethodStatic && !binding.hasContainingType()) @@ -335,9 +350,6 @@ public class CMethodBindingEmitter extends FunctionEmitter { // "this" argument always comes down in argument 0 as direct buffer writer.print(", jobject " + JavaMethodBindingEmitter.javaThisArgumentName()); - numBufferOffsetArgs++; - // add Buffer offset argument for Buffer types - writer.print(", jint " + byteOffsetConversionArgName(numBufferOffsetArgs)); } for (int i = 0; i < binding.getNumArguments(); i++) { JavaType javaArgType = binding.getJavaArgumentType(i); @@ -357,28 +369,13 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(binding.getArgumentName(i)); ++numEmitted; - // Replace following for indirect buffer case - // if(javaArgType.isNIOBuffer() || javaArgType.isNIOBufferArray()) { - if((javaArgType.isNIOBuffer() && !isIndirectBufferInterface()) || javaArgType.isNIOBufferArray()) { - if(!javaArgType.isArray()) { - numBufferOffsetArgs++; - writer.print(", jint " + byteOffsetConversionArgName(numBufferOffsetArgs)); - } else { - numBufferOffsetArrayArgs++; - writer.print(", jintArray " + - byteOffsetArrayConversionArgName(numBufferOffsetArrayArgs)); - } - } - - // indirect buffer case needs same offset syntax as arrays - if(javaArgType.isNIOBuffer() && isIndirectBufferInterface()) - writer.print(", jint " + binding.getArgumentName(i) + "_offset"); - - // Add array primitive index/offset parameter - if(javaArgType.isArray() && !javaArgType.isNIOBufferArray() && !javaArgType.isStringArray()) { - writer.print(", jint " + binding.getArgumentName(i) + "_offset"); - } - + if (javaArgType.isPrimitiveArray() || + javaArgType.isNIOBuffer()) { + writer.print(", jint " + byteOffsetArgName(i)); + } else if (javaArgType.isNIOBufferArray()) { + writer.print(", jintArray " + + byteOffsetArrayArgName(i)); + } } return numEmitted; } @@ -418,7 +415,7 @@ public class CMethodBindingEmitter extends FunctionEmitter continue; } - if (type.isArray() || type.isNIOBuffer()) { + if (type.isArray() || type.isNIOBuffer() || type.isCompoundTypeWrapper()) { String convName = pointerConversionArgumentName(i); // handle array/buffer argument types boolean needsDataCopy = @@ -435,7 +432,7 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.println(" jsize _tmpArrayLen;"); // Pointer to the data in the Buffer, taking the offset into account - writer.println(" GLint * _offsetHandle = NULL;"); + writer.println(" int * _offsetHandle = NULL;"); emittedDataCopyTemps = true; } @@ -457,37 +454,36 @@ public class CMethodBindingEmitter extends FunctionEmitter // Note we must respect const/volatile for return argument writer.print(binding.getCSymbol().getReturnType().getName(true)); writer.println(" _res;"); - if (javaReturnType.isArray()) { - if (javaReturnType.isNIOByteBufferArray()) { - writer.print(" int "); - writer.print(arrayResLength); - writer.println(";"); - writer.print(" int "); - writer.print(arrayIdx); - writer.println(";"); - writer.print(" jobjectArray "); - writer.print(arrayRes); - writer.println(";"); - } else { - writer.print(" int "); - writer.print(arrayResLength); - writer.println(";"); - - Class componentType = javaReturnType.getJavaClass().getComponentType(); - if (componentType.isArray()) { - throw new RuntimeException("Multi-dimensional arrays not supported yet"); - } + if (javaReturnType.isNIOByteBufferArray() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + writer.print(" int "); + writer.print(arrayIdx); + writer.println(";"); + writer.print(" jobjectArray "); + writer.print(arrayRes); + writer.println(";"); + } else if (javaReturnType.isArray()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); - String javaTypeName = componentType.getName(); - capitalizedComponentType = - "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); - String javaArrayTypeName = "j" + javaTypeName + "Array"; - writer.print(" "); - writer.print(javaArrayTypeName); - writer.print(" "); - writer.print(arrayRes); - writer.println(";"); + Class componentType = javaReturnType.getJavaClass().getComponentType(); + if (componentType.isArray()) { + throw new RuntimeException("Multi-dimensional arrays not supported yet"); } + + String javaTypeName = componentType.getName(); + capitalizedComponentType = + "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); + String javaArrayTypeName = "j" + javaTypeName + "Array"; + writer.print(" "); + writer.print(javaArrayTypeName); + writer.print(" "); + writer.print(arrayRes); + writer.println(";"); } } } @@ -504,6 +500,17 @@ public class CMethodBindingEmitter extends FunctionEmitter } } + /** Checks a type (expected to be pointer-to-pointer) for const-ness */ + protected boolean isConstPtrPtr(Type type) { + if (type.pointerDepth() != 2) { + return false; + } + if (type.asPointer().getTargetType().asPointer().getTargetType().isConst()) { + return true; + } + return false; + } + /** * Code to init the variables that were declared in * emitBodyVariableDeclarations(), PRIOR TO calling the actual C @@ -512,21 +519,17 @@ public class CMethodBindingEmitter extends FunctionEmitter protected void emitBodyVariablePreCallSetup(PrintWriter writer, boolean emittingPrimitiveArrayCritical) { - int byteOffsetCounter=0, byteOffsetArrayCounter=0; - if (!emittingPrimitiveArrayCritical) { // Convert all Buffers to pointers first so we don't have to // call ReleasePrimitiveArrayCritical for any arrays if any // incoming buffers aren't direct - // we don't want to fall in here for indirectBuffer case, since its an array - if (binding.hasContainingType() && !isIndirectBufferInterface()) { - byteOffsetCounter++; + if (binding.hasContainingType()) { emitPointerConversion(writer, binding, binding.getContainingType(), binding.getContainingCType(), JavaMethodBindingEmitter.javaThisArgumentName(), CMethodBindingEmitter.cThisArgumentName(), - byteOffsetConversionArgName(byteOffsetCounter)); + null); } for (int i = 0; i < binding.getNumArguments(); i++) { @@ -535,13 +538,13 @@ public class CMethodBindingEmitter extends FunctionEmitter continue; } - if (type.isNIOBuffer() && !isIndirectBufferInterface()) { - byteOffsetCounter++; + if (type.isCompoundTypeWrapper() || + (type.isNIOBuffer() && !forIndirectBufferAndArrayImplementation)) { emitPointerConversion(writer, binding, type, binding.getCArgumentType(i), binding.getArgumentName(i), pointerConversionArgumentName(i), - byteOffsetConversionArgName(byteOffsetCounter)); + byteOffsetArgName(i)); } } } @@ -554,16 +557,9 @@ public class CMethodBindingEmitter extends FunctionEmitter continue; } - // create array replacement type for Buffer for indirect Buffer case - if(isIndirectBufferInterface() && javaArgType.isNIOBuffer()) { - float[] c = new float[1]; - javaArgType = JavaType.createForClass(c.getClass()); - } - - if (javaArgType.isArray()) { + if (javaArgType.isArray() || + (javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation)) { boolean needsDataCopy = javaArgTypeNeedsDataCopy(javaArgType); - Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); - // We only defer the emission of GetPrimitiveArrayCritical // calls that won't be matched up until after the function @@ -588,15 +584,14 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(" "); writer.print(convName); writer.print(" = ("); - if (javaArgType.isArray() && - javaArgType.getJavaClass().getComponentType() == java.lang.String.class) { + if (javaArgType.isStringArray()) { // java-side type is String[] cArgTypeName = "jstring *"; } writer.print(cArgTypeName); - writer.print(") (*env)->GetPrimitiveArrayCritical(env, "); + writer.print(") (((char*) (*env)->GetPrimitiveArrayCritical(env, "); writer.print(binding.getArgumentName(i)); - writer.println(", NULL);"); + writer.println(", NULL)) + " + byteOffsetArgName(i) + ");"); //if(cargtypename is void*) // _ptrX = ((char*)convName + index1*sizeof(thisArgsJavaType)); @@ -607,7 +602,7 @@ public class CMethodBindingEmitter extends FunctionEmitter // // FIXME: should factor out this whole block of code into a separate // method for clarity and maintenance purposes - if (cArgType.toString().indexOf("const") == -1) { + if (!isConstPtrPtr(cArgType)) { // FIXME: if the arg type is non-const, the sematics might be that // the function modifies the argument -- we don't yet support // this. @@ -654,49 +649,43 @@ public class CMethodBindingEmitter extends FunctionEmitter arrayLenName, "Could not allocate buffer for copying data in argument \\\""+binding.getArgumentName(i)+"\\\""); - // Get the handle for the byte offset array sent down for Buffers - // But avoid doing this if the Array is a string array, the one exception since - // that type is never converted to a Buffer according to JOGL semantics (for instance, - // glShaderSourceARB Java signature is String[], not Buffer) - byteOffsetArrayCounter++; - if(subArrayElementJavaType != java.lang.String.class) + // Get the handle for the byte offset array sent down for Buffers + // FIXME: not 100% sure this is correct with respect to the + // JNI spec because it may be illegal to call + // GetObjectArrayElement while in a critical section. May + // need to do another loop and add in the offsets. + if (javaArgType.isNIOBufferArray()) { writer.println - (" _offsetHandle = (GLint *) (*env)->GetPrimitiveArrayCritical(env, " + - byteOffsetArrayConversionArgName(byteOffsetArrayCounter) + - ", NULL);"); - + (" _offsetHandle = (int *) (*env)->GetPrimitiveArrayCritical(env, " + + byteOffsetArrayArgName(i) + + ", NULL);"); + } // process each element in the array writer.println(" for (_copyIndex = 0; _copyIndex < "+arrayLenName+"; ++_copyIndex) {"); // get each array element writer.println(" /* get each element of the array argument \"" + binding.getArgumentName(i) + "\" */"); - String subArrayElementJNITypeString = jniType(subArrayElementJavaType); - writer.print(" _tmpObj = ("); - writer.print(subArrayElementJNITypeString); - writer.print(") (*env)->GetObjectArrayElement(env, "); + writer.print(" _tmpObj = (*env)->GetObjectArrayElement(env, "); writer.print(binding.getArgumentName(i)); writer.println(", _copyIndex);"); - if (subArrayElementJNITypeString == "jstring") - { + if (javaArgType.isStringArray()) { writer.print(" "); emitGetStringUTFChars(writer, "(jstring) _tmpObj", - convName+"_copy[_copyIndex]"); - } - else if (isNIOBufferClass(subArrayElementJavaType)) - { + convName+"_copy[_copyIndex]", + true); + } else if (javaArgType.isNIOBufferArray()) { /* We always assume an integer "byte offset" argument follows any Buffer in the method binding. */ emitGetDirectBufferAddress(writer, "_tmpObj", cArgElementType.getName(), convName + "_copy[_copyIndex]", - "_offsetHandle[_copyIndex]"); - } - else - { + "_offsetHandle[_copyIndex]", + true); + } else { // Question: do we always need to copy the sub-arrays, or just // GetPrimitiveArrayCritical on each jobjectarray element and // assign it to the appropriate elements at pointer depth 1? @@ -704,46 +693,35 @@ public class CMethodBindingEmitter extends FunctionEmitter // Malloc enough space to hold a copy of each sub-array writer.print(" "); emitMalloc( - writer, - convName+"_copy[_copyIndex]", - cArgElementType.getTargetType().getName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! - "(*env)->GetArrayLength(env, _tmpObj)", - "Could not allocate buffer during copying of data in argument \\\""+binding.getArgumentName(i)+"\\\""); + writer, + convName+"_copy[_copyIndex]", + cArgElementType.getTargetType().getName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! + "(*env)->GetArrayLength(env, _tmpObj)", + "Could not allocate buffer during copying of data in argument \\\""+binding.getArgumentName(i)+"\\\""); // FIXME: copy the data (use matched Get/ReleasePrimitiveArrayCritical() calls) if (true) throw new RuntimeException( - "Cannot yet handle type \"" + cArgType.getName() + - "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays"); + "Cannot yet handle type \"" + cArgType.getName() + + "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays"); } writer.println(" }"); - if(subArrayElementJavaType != java.lang.String.class) { - writer.println - (" (*env)->ReleasePrimitiveArrayCritical(env, " + - byteOffsetArrayConversionArgName(byteOffsetArrayCounter) + - ", _offsetHandle, JNI_ABORT);"); + if (javaArgType.isNIOBufferArray()) { + writer.println + (" (*env)->ReleasePrimitiveArrayCritical(env, " + + byteOffsetArrayArgName(i) + + ", _offsetHandle, JNI_ABORT);"); } writer.println(); } // end of data copy if (EMIT_NULL_CHECKS) { - writer.print(" }"); - - if (needsDataCopy) { - writer.println(); - } else { - // Zero out array offset in the case of a null pointer - // being passed down to prevent construction of arbitrary - // pointers - writer.println(" else {"); - writer.println(" " + binding.getArgumentName(i) + "_offset = 0;"); - writer.println(" }"); - } + writer.println(" }"); } } else if (javaArgType.isString()) { - if (emittingPrimitiveArrayCritical) { + if (!emittingPrimitiveArrayCritical) { continue; } @@ -755,7 +733,8 @@ public class CMethodBindingEmitter extends FunctionEmitter emitGetStringUTFChars(writer, binding.getArgumentName(i), - "_UTF8" + binding.getArgumentName(i)); + "_UTF8" + binding.getArgumentName(i), + false); if (EMIT_NULL_CHECKS) { writer.println(" }"); @@ -783,16 +762,9 @@ public class CMethodBindingEmitter extends FunctionEmitter continue; } - - // create array type for Indirect Buffer case - if(isIndirectBufferInterface() && javaArgType.isNIOBuffer()) { - float[] c = new float[1]; - javaArgType = JavaType.createForClass(c.getClass()); - } - - if (javaArgType.isArray()) { + if (javaArgType.isArray() || + (javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation)) { boolean needsDataCopy = javaArgTypeNeedsDataCopy(javaArgType); - Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); if ((!needsDataCopy && !emittingPrimitiveArrayCritical) || (needsDataCopy && emittingPrimitiveArrayCritical)) { @@ -838,7 +810,7 @@ public class CMethodBindingEmitter extends FunctionEmitter // Only need to perform cleanup for individual array // elements if they are not direct buffers - if (!isNIOBufferClass(subArrayElementJavaType)) { + if (!javaArgType.isNIOBufferArray()) { // Re-fetch length of array that was copied String arrayLenName = "_tmpArrayLen"; writer.print(" "); @@ -861,14 +833,11 @@ public class CMethodBindingEmitter extends FunctionEmitter // get each array element writer.println(" /* free each element of " +convName +"_copy */"); - String subArrayElementJNITypeString = jniType(subArrayElementJavaType); - writer.print(" _tmpObj = ("); - writer.print(subArrayElementJNITypeString); - writer.print(") (*env)->GetObjectArrayElement(env, "); + writer.print(" _tmpObj = (*env)->GetObjectArrayElement(env, "); writer.print(binding.getArgumentName(i)); writer.println(", _copyIndex);"); - if (subArrayElementJNITypeString == "jstring") { + if (javaArgType.isStringArray()) { writer.print(" (*env)->ReleaseStringUTFChars(env, "); writer.print("(jstring) _tmpObj"); writer.print(", "); @@ -920,25 +889,9 @@ public class CMethodBindingEmitter extends FunctionEmitter } } - protected void emitBodyCallCFunction(PrintWriter writer) - { - - // Make the call to the actual C function - writer.print(" "); - - // WARNING: this code assumes that the return type has already been - // typedef-resolved. - Type cReturnType = binding.getCReturnType(); - - if (!cReturnType.isVoid()) { - writer.print("_res = "); - } - if (binding.hasContainingType()) { - // Call through function pointer - writer.print(CMethodBindingEmitter.cThisArgumentName() + "->"); - } - writer.print(binding.getCSymbol().getName()); - writer.print("("); + /** Returns the number of arguments passed so calling code knows + whether to print a comma */ + protected int emitBodyPassCArguments(PrintWriter writer) { for (int i = 0; i < binding.getNumArguments(); i++) { if (i != 0) { writer.print(", "); @@ -964,40 +917,39 @@ public class CMethodBindingEmitter extends FunctionEmitter if (binding.getCArgumentType(i).isPointer() && binding.getJavaArgumentType(i).isPrimitive()) { writer.print("(intptr_t) "); } - if (javaArgType.isArray() || javaArgType.isNIOBuffer()) { - - // Add special code for accounting for array offsets - // - // For mapping from byte primitive array type to type* case produces code: - // (GLtype*)((char*)_ptr0 + varName_offset) - // where varName_offset is the number of bytes offset as calculated in Java code - // also, output same for indirect buffer as for array - if(javaArgType.isNIOBuffer() && isIndirectBufferInterface()) - writer.print("( (char*)"); - else if(javaArgType.isArray() && !javaArgType.isNIOBufferArray() && !javaArgType.isStringArray()) { - writer.print("( (char*)"); - } - /* End of this section of new code for array offsets */ - - writer.print(pointerConversionArgumentName(i)); - if (javaArgTypeNeedsDataCopy(javaArgType)) { - writer.print("_copy"); - } - - /* Continuation of special code for accounting for array offsets */ - if(javaArgType.isNIOBuffer() && isIndirectBufferInterface()) - writer.print(" + " + binding.getArgumentName(i) + "_offset)"); - else if(javaArgType.isArray() && !javaArgType.isNIOBufferArray() && !javaArgType.isStringArray()) { - writer.print(" + " + binding.getArgumentName(i) + "_offset)"); - } - /* End of this section of new code for array offsets */ - + if (javaArgType.isArray() || javaArgType.isNIOBuffer() || javaArgType.isCompoundTypeWrapper()) { + writer.print(pointerConversionArgumentName(i)); + if (javaArgTypeNeedsDataCopy(javaArgType)) { + writer.print("_copy"); + } } else { if (javaArgType.isString()) { writer.print("_UTF8"); } writer.print(binding.getArgumentName(i)); } } } + return binding.getNumArguments(); + } + + protected void emitBodyCallCFunction(PrintWriter writer) { + + // Make the call to the actual C function + writer.print(" "); + + // WARNING: this code assumes that the return type has already been + // typedef-resolved. + Type cReturnType = binding.getCReturnType(); + + if (!cReturnType.isVoid()) { + writer.print("_res = "); + } + if (binding.hasContainingType()) { + // Call through function pointer + writer.print(CMethodBindingEmitter.cThisArgumentName() + "->"); + } + writer.print(binding.getCSymbol().getName()); + writer.print("("); + emitBodyPassCArguments(writer); writer.println(");"); } @@ -1013,9 +965,6 @@ public class CMethodBindingEmitter extends FunctionEmitter } } - // FIXME: refactor this so that subclasses (in particular, - // net.java.games.gluegen.opengl.CGLPAWrapperEmitter) don't have to copy the whole - // method protected void emitBodyReturnResult(PrintWriter writer) { // WARNING: this code assumes that the return type has already been @@ -1033,7 +982,8 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print("(" + javaReturnType.jniTypeName() + ") (intptr_t) "); } writer.println("_res;"); - } else if (javaReturnType.isNIOBuffer()) { + } else if (javaReturnType.isNIOBuffer() || + javaReturnType.isCompoundTypeWrapper()) { writer.println(" if (_res == NULL) return NULL;"); writer.print(" return (*env)->NewDirectByteBuffer(env, _res, "); // See whether capacity has been specified @@ -1051,7 +1001,7 @@ public class CMethodBindingEmitter extends FunctionEmitter cReturnType.asPointer().getTargetType().isCompound()) { sz = cReturnType.asPointer().getTargetType().getSize(); if (sz == -1) { - throw new InternalError( + throw new RuntimeException( "Error emitting code for compound return type "+ "for function \"" + binding + "\": " + "Structs to be emitted should have been laid out by this point " + @@ -1072,64 +1022,67 @@ public class CMethodBindingEmitter extends FunctionEmitter } else if (javaReturnType.isString()) { writer.print(" if (_res == NULL) return NULL;"); writer.println(" return (*env)->NewStringUTF(env, _res);"); - } else if (javaReturnType.isArray()) { - if (javaReturnType.isNIOByteBufferArray()) { - writer.println(" if (_res == NULL) return NULL;"); - if (returnValueLengthExpression == null) { - throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + - binding); - } - String[] argumentNames = new String[binding.getNumArguments()]; - for (int i = 0; i < binding.getNumArguments(); i++) { - argumentNames[i] = binding.getArgumentName(i); - } - writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNames) + ";"); - writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); - writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); - Type retType = binding.getCSymbol().getReturnType(); - Type baseType; - if (retType.isPointer()) { - baseType = retType.asPointer().getTargetType().asPointer().getTargetType(); - } else { - baseType = retType.asArray().getElementType().asPointer().getTargetType(); - } - int sz = baseType.getSize(); - if (sz < 0) - sz = 0; - writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + - ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], " + sz + "));"); - writer.println(" }"); - writer.println(" return " + arrayRes + ";"); + } else if (javaReturnType.isArrayOfCompoundTypeWrappers() || + (javaReturnType.isArray() && javaReturnType.isNIOByteBufferArray())) { + writer.println(" if (_res == NULL) return NULL;"); + if (returnValueLengthExpression == null) { + throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + + binding); + } + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNames) + ";"); + writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); + writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); + Type retType = binding.getCSymbol().getReturnType(); + Type baseType; + if (retType.isPointer()) { + baseType = retType.asPointer().getTargetType().asPointer().getTargetType(); } else { - // FIXME: must have user provide length of array in .cfg file - // by providing a constant value, input parameter, or - // expression which computes the array size (already present - // as ReturnValueCapacity, not yet implemented / tested here) - - throw new RuntimeException( - "Could not emit native code for function \"" + binding + - "\": array return values for non-char types not implemented yet"); - - // FIXME: This is approximately what will be required here - // - //writer.print(" "); - //writer.print(arrayRes); - //writer.print(" = (*env)->New"); - //writer.print(capitalizedComponentType); - //writer.print("Array(env, "); - //writer.print(arrayResLength); - //writer.println(");"); - //writer.print(" (*env)->Set"); - //writer.print(capitalizedComponentType); - //writer.print("ArrayRegion(env, "); - //writer.print(arrayRes); - //writer.print(", 0, "); - //writer.print(arrayResLength); - //writer.println(", _res);"); - //writer.print(" return "); - //writer.print(arrayRes); - //writer.println(";"); + baseType = retType.asArray().getElementType().asPointer().getTargetType(); } + int sz = baseType.getSize(); + if (sz < 0) + sz = 0; + writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + + ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], " + sz + "));"); + writer.println(" }"); + writer.println(" return " + arrayRes + ";"); + } else if (javaReturnType.isArray()) { + // FIXME: must have user provide length of array in .cfg file + // by providing a constant value, input parameter, or + // expression which computes the array size (already present + // as ReturnValueCapacity, not yet implemented / tested here) + + throw new RuntimeException( + "Could not emit native code for function \"" + binding + + "\": array return values for non-char types not implemented yet"); + + // FIXME: This is approximately what will be required here + // + //writer.print(" "); + //writer.print(arrayRes); + //writer.print(" = (*env)->New"); + //writer.print(capitalizedComponentType); + //writer.print("Array(env, "); + //writer.print(arrayResLength); + //writer.println(");"); + //writer.print(" (*env)->Set"); + //writer.print(capitalizedComponentType); + //writer.print("ArrayRegion(env, "); + //writer.print(arrayRes); + //writer.print(", 0, "); + //writer.print(arrayResLength); + //writer.println(", _res);"); + //writer.print(" return "); + //writer.print(arrayRes); + //writer.println(";"); + } else { + System.err.print("Unhandled return type: "); + javaReturnType.dump(); + throw new RuntimeException("Unhandled return type"); } } } @@ -1146,38 +1099,56 @@ public class CMethodBindingEmitter extends FunctionEmitter protected String jniMangle(MethodBinding binding) { StringBuffer buf = new StringBuffer(); buf.append(jniMangle(binding.getName())); + buf.append(getImplSuffix()); buf.append("__"); + if (binding.hasContainingType()) { + // "this" argument always comes down in argument 0 as direct buffer + jniMangle(java.nio.ByteBuffer.class, buf, true); + } for (int i = 0; i < binding.getNumArguments(); i++) { + if (binding.isArgumentThisPointer(i)) { + continue; + } JavaType type = binding.getJavaArgumentType(i); - Class c = type.getJavaClass(); - if (c != null) { - jniMangle(c, buf); - // If Buffer offset arguments were added, we need to mangle the JNI for the - // extra arguments - if(type.isNIOBuffer()) { - jniMangle(Integer.TYPE, buf); - } else if (type.isNIOBufferArray()) { - int[] intArrayType = new int[0]; - c = intArrayType.getClass(); - jniMangle(c , buf); - } - if(type.isArray() && !type.isNIOBufferArray()) { - jniMangle(Integer.TYPE, buf); - } + if (type.isVoid()) { + // We should only see "void" as the first argument of a 1-argument function + // FIXME: should normalize this in the parser + if ((i != 0) || (binding.getNumArguments() > 1)) { + throw new RuntimeException("Saw illegal \"void\" argument while emitting \"" + binding.getName() + "\""); + } } else { - // FIXME: add support for char* -> String conversion - throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); + Class c = type.getJavaClass(); + if (c != null) { + jniMangle(c, buf, false); + // If Buffer offset arguments were added, we need to mangle the JNI for the + // extra arguments + if (type.isNIOBuffer()) { + jniMangle(Integer.TYPE, buf, false); + } else if (type.isNIOBufferArray()) { + int[] intArrayType = new int[0]; + c = intArrayType.getClass(); + jniMangle(c , buf, true); + } + if (type.isPrimitiveArray()) { + jniMangle(Integer.TYPE, buf, false); + } + } else if (type.isCompoundTypeWrapper()) { + // Mangle wrappers for C structs as ByteBuffer + jniMangle(java.nio.ByteBuffer.class, buf, true); + } else if (type.isJNIEnv()) { + // These are not exposed at the Java level + } else { + // FIXME: add support for char* -> String conversion + throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); + } } } return buf.toString(); } - protected void jniMangle(Class c, StringBuffer res) { - if (c.isArray()) { - res.append("_3"); - jniMangle(c.getComponentType(), res); - } else if (c.isPrimitive()) { + protected void jniMangle(Class c, StringBuffer res, boolean syntheticArgument) { + if (c.isPrimitive()) { if (c == Boolean.TYPE) res.append("Z"); else if (c == Byte.TYPE) res.append("B"); else if (c == Character.TYPE) res.append("C"); @@ -1186,35 +1157,37 @@ public class CMethodBindingEmitter extends FunctionEmitter else if (c == Long.TYPE) res.append("J"); else if (c == Float.TYPE) res.append("F"); else if (c == Double.TYPE) res.append("D"); - else throw new InternalError("Illegal primitive type"); + else throw new RuntimeException("Illegal primitive type \"" + c.getName() + "\""); } else { - if(!isIndirectBufferInterface()) { - res.append("L"); - res.append(c.getName().replace('.', '_')); - res.append("_2"); - } else { // indirect buffer sends array as object - res.append("L"); - res.append("java_lang_Object"); - res.append("_2"); - } + // Arrays and NIO Buffers are always passed down as java.lang.Object. + // The only arrays that show up as true arrays in the signature + // are the synthetic byte offset arrays created when passing + // down arrays of direct Buffers. Compound type wrappers are + // passed down as ByteBuffers (no good reason, just to avoid + // accidental conflation) so we mangle them differently. + if (syntheticArgument) { + if (c.isArray()) { + res.append("_3"); + jniMangle(c.getComponentType(), res, false); + } else { + res.append("L"); + res.append(c.getName().replace('.', '_')); + res.append("_2"); + } + } else { + if (c == java.lang.String.class) { + res.append("L"); + res.append(c.getName().replace('.', '_')); + res.append("_2"); + } else { + res.append("L"); + res.append("java_lang_Object"); + res.append("_2"); + } + } } } - private String jniType(Class javaType) - { - if (javaType.isPrimitive()) { - return "j" + javaType.getName(); - } else if (javaType == java.lang.String.class) { - return "jstring"; - } else if (isNIOBufferClass(javaType)) { - return "jobject"; - } else { - throw new RuntimeException( - "Could not determine JNI type for Java class \"" + - javaType.getName() + "\"; was not String, primitive or direct buffer"); - } - } - private void emitOutOfMemoryCheck(PrintWriter writer, String varName, String errorMessage) { @@ -1259,11 +1232,14 @@ public class CMethodBindingEmitter extends FunctionEmitter private void emitGetStringUTFChars(PrintWriter writer, String sourceVarName, - String receivingVarName) + String receivingVarName, + boolean emitElseClause) { - writer.print(" if ("); - writer.print(sourceVarName); - writer.println(" != NULL) {"); + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(sourceVarName); + writer.println(" != NULL) {"); + } writer.print(" "); writer.print(receivingVarName); writer.print(" = (*env)->GetStringUTFChars(env, "); @@ -1276,11 +1252,18 @@ public class CMethodBindingEmitter extends FunctionEmitter writer, receivingVarName, "Failed to get UTF-8 chars for argument \\\""+sourceVarName+"\\\""); } - writer.println(" } else {"); - writer.print(" "); - writer.print(receivingVarName); - writer.println(" = NULL;"); - writer.println(" }"); + if (EMIT_NULL_CHECKS) { + writer.print(" }"); + if (emitElseClause) { + writer.print(" else {"); + writer.print(" "); + writer.print(receivingVarName); + writer.println(" = NULL;"); + writer.println(" }"); + } else { + writer.println(); + } + } } @@ -1289,56 +1272,37 @@ public class CMethodBindingEmitter extends FunctionEmitter String sourceVarName, String receivingVarTypeString, String receivingVarName, - String byteOffsetVarName) { + String byteOffsetVarName, + boolean emitElseClause) { if (EMIT_NULL_CHECKS) { - writer.print(" if ("); + writer.print(" if ("); writer.print(sourceVarName); writer.println(" != NULL) {"); + writer.print(" "); } - /* Pre Buffer Offset code: In the case where there is NOT an integer offset - in bytes for the direct buffer, we used to use: - (type*) (*env)->GetDirectBufferAddress(env, buf); - generated as follows: - if(byteOffsetVarName == null) { - writer.print(" "); - writer.print(receivingVarName); - writer.print(" = ("); - writer.print(receivingVarTypeString); - writer.print(") (*env)->GetDirectBufferAddress(env, "); - writer.print(sourceVarName); - writer.println(");"); - */ - - /* In the case (now always the case) where there is an integer offset in bytes for - the direct buffer, we want to use: - _ptrX = (type*) (*env)->GetDirectBufferAddress(env, buf); - _ptrX = (type*) ((char*)buf + __byteOffset); - Note that __byteOffset is an int */ - writer.print(" "); - writer.print(receivingVarName); - writer.print(" = ("); - writer.print(receivingVarTypeString); - - writer.print(") (*env)->GetDirectBufferAddress(env, "); - writer.print(sourceVarName); - writer.println(");"); - - writer.print(" "); - writer.print(receivingVarName); - writer.print(" = ("); - writer.print(receivingVarTypeString); - writer.print(") ((char*)" + receivingVarName + " + "); - writer.println(byteOffsetVarName + ");"); + + writer.print(" "); + writer.print(receivingVarName); + writer.print(" = ("); + writer.print(receivingVarTypeString); + + writer.print(") (((char*) (*env)->GetDirectBufferAddress(env, "); + writer.print(sourceVarName); + writer.println(")) + " + ((byteOffsetVarName != null) ? byteOffsetVarName : "0") + ");"); if (EMIT_NULL_CHECKS) { - writer.println(" } else {"); - writer.print(" "); - writer.print(receivingVarName); - writer.println(" = NULL;"); - writer.println(" }"); + writer.print(" }"); + if (emitElseClause) { + writer.println(" else {"); + writer.print(" "); + writer.print(receivingVarName); + writer.println(" = NULL;"); + writer.println(" }"); + } else { + writer.println(); + } } } - // Note: if the data in the Type needs to be converted from the Java memory // model to the C memory model prior to calling any C-side functions, then @@ -1356,80 +1320,54 @@ public class CMethodBindingEmitter extends FunctionEmitter // // Note that we don't need to obey const/volatile for outgoing arguments // - if (javaType.isNIOBuffer()) - { + if (javaType.isNIOBuffer()) { ptrTypeString = cType.getName(); - } - else if (javaType.isArray()) { + } else if (javaType.isArray()) { needsDataCopy = javaArgTypeNeedsDataCopy(javaType); - // It's an array; get the type of the elements in the array - Class elementType = javaType.getJavaClass().getComponentType(); - if (elementType.isPrimitive()) - { + if (javaType.isPrimitiveArray() || + javaType.isNIOBufferArray()) { ptrTypeString = cType.getName(); - } - else if (elementType == java.lang.String.class) - { - ptrTypeString = "jstring"; - } - else if (elementType.isArray()) - { - Class subElementType = elementType.getComponentType(); - if (subElementType.isPrimitive()) - { - // type is pointer to pointer to primitive - ptrTypeString = cType.getName(); - } - else - { + } else if (!javaType.isStringArray()) { + Class elementType = javaType.getJavaClass().getComponentType(); + if (elementType.isArray()) { + Class subElementType = elementType.getComponentType(); + if (subElementType.isPrimitive()) { + // type is pointer to pointer to primitive + ptrTypeString = cType.getName(); + } else { + // type is pointer to pointer of some type we don't support (maybe + // it's an array of pointers to structs?) + throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + } + } else { // type is pointer to pointer of some type we don't support (maybe // it's an array of pointers to structs?) throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); } - - } - else if (isNIOBufferClass(elementType)) - { - // type is an array of direct buffers of some sort - ptrTypeString = cType.getName(); } - else - { - // Type is pointer to something we can't/don't handle - throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); - } - } - else if (javaType.isArrayOfCompoundTypeWrappers()) - { + } else if (javaType.isArrayOfCompoundTypeWrappers()) { // FIXME throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); - } - else - { + } else { ptrTypeString = cType.getName(); } - if (!needsDataCopy) - { + if (!needsDataCopy) { // declare the pointer variable writer.print(" "); writer.print(ptrTypeString); writer.print(" "); writer.print(cVariableName); writer.println(" = NULL;"); - } - else - { + } else { // Declare a variable to hold a copy of the argument data in which the // incoming data has been properly laid out in memory to match the C // memory model - //writer.print(" const "); Class elementType = javaType.getJavaClass().getComponentType(); - if (javaType.isArray() && - javaType.getJavaClass().getComponentType() == java.lang.String.class) { + if (javaType.isStringArray()) { writer.print(" const char **"); } else { - writer.print(ptrTypeString); + writer.print(" " + ptrTypeString); } writer.print(" "); writer.print(cVariableName); @@ -1448,45 +1386,34 @@ public class CMethodBindingEmitter extends FunctionEmitter String incomingArgumentName, String cVariableName, String byteOffsetVarName) { + // Compound type wrappers do not get byte offsets added on + if (type.isCompoundTypeWrapper()) { + byteOffsetVarName = null; + } + emitGetDirectBufferAddress(writer, incomingArgumentName, cType.getName(), cVariableName, - byteOffsetVarName); - - /* - if (EMIT_NULL_CHECKS) { - writer.print(" if ("); - writer.print(incomingArgumentName); - writer.println(" != NULL) {"); - } - - writer.print(" "); - writer.print(cVariableName); - writer.print(" = ("); - writer.print(cType.getName()); - writer.print(") (*env)->GetDirectBufferAddress(env, "); - writer.print(incomingArgumentName); - writer.println(");"); - - if (EMIT_NULL_CHECKS) { - writer.println(" }"); - } - */ + byteOffsetVarName, + false); } - protected String pointerConversionArgumentName(int i) { - return "_ptr" + i; + protected String byteOffsetArgName(int i) { + return byteOffsetArgName(binding.getArgumentName(i)); } - protected String byteOffsetConversionArgName(int i) { - return "__byteOffset" + i; + protected String byteOffsetArgName(String s) { + return s + "_byte_offset"; } - - protected String byteOffsetArrayConversionArgName(int i) { - return "__byteOffsetArray" + i; + + protected String byteOffsetArrayArgName(int i) { + return binding.getArgumentName(i) + "_byte_offset_array"; + } + + protected String pointerConversionArgumentName(int i) { + return "_ptr" + i; } - /** * Class that emits a generic comment for CMethodBindingEmitters; the comment @@ -1515,16 +1442,10 @@ public class CMethodBindingEmitter extends FunctionEmitter protected boolean javaArgTypeNeedsDataCopy(JavaType javaArgType) { if (javaArgType.isArray()) { - Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); - return (subArrayElementJavaType.isArray() || - subArrayElementJavaType == java.lang.String.class || - isNIOBufferClass(subArrayElementJavaType)); + return (javaArgType.isNIOBufferArray() || + javaArgType.isStringArray() || + javaArgType.getJavaClass().getComponentType().isArray()); } return false; } - - protected static boolean isNIOBufferClass(Class c) { - return java.nio.Buffer.class.isAssignableFrom(c); - } } - diff --git a/src/net/java/games/gluegen/CMethodBindingImplEmitter.java b/src/net/java/games/gluegen/CMethodBindingImplEmitter.java deleted file mode 100644 index 3f3505896..000000000 --- a/src/net/java/games/gluegen/CMethodBindingImplEmitter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package net.java.games.gluegen; - -import java.util.*; -import java.io.*; -import java.text.MessageFormat; - -public class CMethodBindingImplEmitter extends CMethodBindingEmitter -{ - protected static final CommentEmitter defaultCImplCommentEmitter = - new CImplCommentEmitter(); - - protected boolean arrayImplRoutine = false; - - public CMethodBindingImplEmitter(MethodBinding binding, - boolean isOverloadedBinding, - boolean arrayImpl, - String javaPackageName, - String javaClassName, - boolean isJavaMethodStatic, - PrintWriter output) - { - super(binding, isOverloadedBinding, - javaPackageName, javaClassName, - isJavaMethodStatic, output); - setCommentEmitter(defaultCImplCommentEmitter); - arrayImplRoutine = arrayImpl; - } - - protected void emitName(PrintWriter writer) - { - super.emitName(writer); - if (!getIsOverloadedBinding()) { - if( isIndirectBufferInterface() ) - writer.print("2"); - else if(!arrayImplRoutine) - writer.print("0"); - else - writer.print("1"); - } - } - - /** - * Gets the mangled name for the binding, but assumes that this is an Impl - * routine - */ - protected String jniMangle(MethodBinding binding) { - StringBuffer buf = new StringBuffer(); - buf.append(jniMangle(binding.getName())); - - if( isIndirectBufferInterface() ) - buf.append("2"); - else if(!arrayImplRoutine) - buf.append("0"); - else - buf.append("1"); - - buf.append("__"); - for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); - - Class c = type.getJavaClass(); - if (c != null) { - jniMangle(c, buf); - // Add mangling for buffer offset arguments - if(type.isNIOBuffer()) { - jniMangle(Integer.TYPE, buf); - } else if (type.isNIOBufferArray()) { - int[] intArrayType = new int[0]; - c = intArrayType.getClass(); - jniMangle(c , buf); - } - if(type.isArray() && !type.isNIOBufferArray() && !type.isStringArray()) { - jniMangle(Integer.TYPE, buf); - } - } else { - // FIXME: add support for char* -> String conversion - throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); - } - } - return buf.toString(); - } - - protected static class CImplCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { - protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { - writer.print(" -- FIXME: PUT A COMMENT HERE -- "); - } - } -} diff --git a/src/net/java/games/gluegen/JavaConfiguration.java b/src/net/java/games/gluegen/JavaConfiguration.java index e39a147f8..51e22f7d6 100644 --- a/src/net/java/games/gluegen/JavaConfiguration.java +++ b/src/net/java/games/gluegen/JavaConfiguration.java @@ -50,28 +50,6 @@ import net.java.games.gluegen.cgram.types.*; public class JavaConfiguration { - /* possible PrimitiveArrayExpansionMode states */ - private int ALL_POINTERS = 1; - private int NON_VOID_ONLY = 2; - private int NO_POINTERS = 3; - - /* Default primitive array expansion mode is ALL_POINTERS, meaning all C - pointers, including void pointers, will have Java primitive array - expansions */ - private int primitiveArrayExpansionMode = ALL_POINTERS; - - /* Determines which primitive array types void * C signatures are - expanded into (assuming PrimitiveArrayExpansionMode = ALL_POINTERS). */ - private boolean voidPointerExpansionToBoolean = true; - private boolean voidPointerExpansionToChar = true; - private boolean voidPointerExpansionToByte = true; - private boolean voidPointerExpansionToShort = true; - private boolean voidPointerExpansionToInt = true; - private boolean voidPointerExpansionToLong = true; - private boolean voidPointerExpansionToFloat = true; - private boolean voidPointerExpansionToDouble = true; - - private int nestedReads; private String packageName; private String implPackageName; @@ -120,21 +98,12 @@ public class JavaConfiguration { private Set/*<Pattern>*/ ignoreNots = new HashSet(); private Set/*<Pattern>*/ unimplemented = new HashSet(); private Set/*<String>*/ nioDirectOnly = new HashSet(); - /** See {@link #nioMode} */ - public static final int NIO_MODE_VOID_ONLY = 1; - /** See {@link #nioMode} */ - public static final int NIO_MODE_ALL_POINTERS = 2; - private int nioMode = NIO_MODE_VOID_ONLY; - private Set/*<String>*/ noNio = new HashSet(); - private Set/*<String>*/ forcedNio = new HashSet(); - private boolean flattenNIOVariants = true; private Set/*<String>*/ manuallyImplement = new HashSet(); private Map/*<String,List<String>>*/ customJavaCode = new HashMap(); private Map/*<String,List<String>>*/ classJavadoc = new HashMap(); private Map/*<String,String>*/ structPackages = new HashMap(); private List/*<String>*/ customCCode = new ArrayList(); private List/*<String>*/ forcedStructs = new ArrayList(); - private Map/*<String,List<Integer>>*/ mirroredArgs = new HashMap(); private Map/*<String, String>*/ returnValueCapacities = new HashMap(); private Map/*<String, String>*/ returnValueLengths = new HashMap(); private Map/*<String, List<String>>*/ temporaryCVariableDeclarations = new HashMap(); @@ -253,30 +222,6 @@ public class JavaConfiguration { /** Returns the list of imports that should be emitted at the top of each .java file. */ public List/*<String>*/ imports() { return imports; } - - /* Return Primitive Array Expansion Mode state */ - public boolean isPrimArrayExpModeAllPtrs() { - return (primitiveArrayExpansionMode == ALL_POINTERS); - } - public boolean isPrimArrayExpModeNonVoidPtrs() { - return (primitiveArrayExpansionMode == NON_VOID_ONLY); - } - public boolean isPrimArrayExpModeNoPtrs() { - return (primitiveArrayExpansionMode == NO_POINTERS); - } - - - /** Returns VoidPointerExpansion values */ - public boolean voidPointerExpansionToBoolean() { return voidPointerExpansionToBoolean; } - public boolean voidPointerExpansionToChar() { return voidPointerExpansionToChar; } - public boolean voidPointerExpansionToByte() { return voidPointerExpansionToByte; } - public boolean voidPointerExpansionToShort() { return voidPointerExpansionToShort; } - public boolean voidPointerExpansionToInt() { return voidPointerExpansionToInt; } - public boolean voidPointerExpansionToLong() { return voidPointerExpansionToLong; } - public boolean voidPointerExpansionToFloat() { return voidPointerExpansionToFloat; } - public boolean voidPointerExpansionToDouble() { return voidPointerExpansionToDouble; } - - /** If this type should be considered opaque, returns the TypeInfo describing the replacement type. Returns null if this type should not be considered opaque. */ @@ -367,40 +312,6 @@ public class JavaConfiguration { return nioDirectOnly.contains(functionName); } - /** Returns true if the user requested that the given function - should only create array variants, and no java.nio variant, for - <code>void*</code> and other C primitive pointers, overriding - the NIO mode default. */ - public boolean noNio(String functionName) { - return noNio.contains(functionName); - } - - /** Returns true if the user requested that the given function - should create a java.nio variant for the given function's - <code>void*</code> and other C primitive pointers, overriding - the NIO mode default. */ - public boolean forcedNio(String functionName) { - return forcedNio.contains(functionName); - } - - /** Returns the default NIO generation mode for C primitive pointer - arguments. NIO_MODE_VOID_ONLY is the default and specifies - that only void* arguments will have java.nio variants generated - for them. NIO_MODE_ALL_POINTERS specifies that all C - primitive arguments will have java.nio variants generated. */ - public int nioMode() { - return nioMode; - } - - /** Returns true if, for the plethora of java.nio variants generated - for primitive C pointer types, the emitter should flatten the - output down to two variants: one taking only Java primitive - arrays as arguments, and one taking only java.nio.Buffers as - arguments. */ - public boolean flattenNIOVariants() { - return flattenNIOVariants; - } - /** Returns true if the glue code for the given function will be manually implemented by the end user. */ public boolean manuallyImplement(String functionName) { @@ -456,14 +367,6 @@ public class JavaConfiguration { return forcedStructs; } - /** Returns a List of Integers indicating the indices of arguments - in this function that should be expanded to the same type when - binding functions with multiple void* arguments. Returns null if - no such indices were specified. */ - public List/*<Integer>*/ mirroredArgs(String functionName) { - return (List) mirroredArgs.get(functionName); - } - /** Returns a MessageFormat string of the C expression calculating the capacity of the java.nio.ByteBuffer being returned from a native method, or null if no expression has been specified. */ @@ -676,22 +579,8 @@ public class JavaConfiguration { // because readClassJavadoc changes them. } else if (cmd.equalsIgnoreCase("NioDirectOnly")) { nioDirectOnly.add(readString("NioDirectOnly", tok, filename, lineNo)); - } else if (cmd.equalsIgnoreCase("NoNio")) { - noNio.add(readString("NoNio", tok, filename, lineNo)); - } else if (cmd.equalsIgnoreCase("ForcedNio")) { - forcedNio.add(readString("ForcedNio", tok, filename, lineNo)); - } else if (cmd.equalsIgnoreCase("NioMode")) { - readNioMode(tok, filename, lineNo); - } else if (cmd.equalsIgnoreCase("FlattenNIOVariants")) { - flattenNIOVariants = readBoolean("FlattenNIOVariants", tok, filename, lineNo).booleanValue(); - } else if (cmd.equalsIgnoreCase("PrimitiveArrayExpansionMode")) { - readPrimitiveExpMode(tok, filename, lineNo); - } else if (cmd.equalsIgnoreCase("VoidPointerExpansion")) { - readVoidPointerExpansionSet(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("EmitStruct")) { forcedStructs.add(readString("EmitStruct", tok, filename, lineNo)); - } else if (cmd.equalsIgnoreCase("MirrorExpandedBindingArgs")) { - readMirrorExpandedBindingArgs(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("StructPackage")) { readStructPackage(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("TemporaryCVariableDeclaration")) { @@ -899,226 +788,6 @@ public class JavaConfiguration { codeList.add(code); } - /** - * Sets the default NIO generation mode for C primitive - * pointers. Options are VOID_ONLY or ALL_POINTERS. When the mode is - * set to VOID_ONLY, java.nio variants of methods are only generated - * for C primitive pointers of type <code>void*</code>. All other - * pointers are translated by default into Java primitive arrays. - * When the mode is set to ALL_POINTERS, C primitive pointers of - * other types (i.e., <code>int*</code>) will have java.nio variants - * generated for them (i.e., <code>IntBuffer</code> as opposed to - * merely <code>int[]</code>). This default mode can be overridden - * with the NioDirectOnly and NoNio directives. The default for this mode - * is currently VOID_ONLY. - */ - protected void readNioMode(StringTokenizer tok, String filename, int lineNo) { - try { - String mode = tok.nextToken(); - if (mode.equalsIgnoreCase("VOID_ONLY")) { - nioMode = NIO_MODE_VOID_ONLY; - } else if (mode.equalsIgnoreCase("ALL_POINTERS")) { - nioMode = NIO_MODE_ALL_POINTERS; - } else { - throw new RuntimeException("Error parsing \"NioMode\" command at line " + lineNo + - " in file \"" + filename + "\"; expected VOID_ONLY or ALL_POINTERS"); - } - } catch (NoSuchElementException e) { - throw new RuntimeException( - "Error parsing \"NioMode\" command at line " + lineNo + - " in file \"" + filename + "\"", e); - } - } - - - /** - * Sets the Primitive Array Expansion Mode. Options are ALL_POINTERS (default), - * NON_VOID_ONLY, and NO_POINTERS. ALL_POINTERS means that all Primitive array - * C pointers (float*, int*, void*, etc.) get expanded into non-NIO buffer - * targets. The setting has no bearing on whether NIO buffer targets happen. - * So float * goes to float[], int* goes to int[], and void* gets expanded to - * (by default) double[], float[], long[], int[], short[], byte[], char[], and - * boolean[]. NON_VOID_ONLY means that all Primitive array pointers except - * for void * get expanded, and NO_POINTERS means that no C Primitive array - * pointers get expanded. - * For void * expansion the default is all 8 primitive types as listed above. - * However, the types can be restricted by use of the VoidPointerExpansion - * attribute defined elsewhere in this file. - */ - - protected void readPrimitiveExpMode(StringTokenizer tok, String filename, int lineNo) { - try { - String mode = tok.nextToken(); - if (mode.equalsIgnoreCase("ALL_POINTERS")) { - primitiveArrayExpansionMode = ALL_POINTERS; - } else if (mode.equalsIgnoreCase("NON_VOID_ONLY")) { - primitiveArrayExpansionMode = NON_VOID_ONLY; - } else if (mode.equalsIgnoreCase("NO_POINTERS")) { - primitiveArrayExpansionMode = NO_POINTERS; - } else { - throw new RuntimeException("Error parsing \"PrimitiveArrayExpansionMode\" command at line " + lineNo + - " in file \"" + filename - + "\"; expected NO_POINTERS, NON_VOID_ONLY or ALL_POINTERS"); - } - } catch (NoSuchElementException e) { - throw new RuntimeException( - "Error parsing \"PrimitiveArrayExpansionMode\" command at line " + lineNo + - " in file \"" + filename + "\"", e); - } - } - - - - /** - * readVoidPointerExpansionSet: - * Parses VoidPointerExpansion arguments. Expecting subset of boolean, char, byte, - * short, int, long,float, double. Example grammar file syntax: - * VoidPointerExpansion short int float byte double - * If PrimitiveArrayExpansionMode is set to NON_VOID_ONLY or NO_POINTERS, then - * the VoidPointerExpansion attribute has no effect, since in that case void - * pointers are not expanded. - */ - - protected void readVoidPointerExpansionSet(StringTokenizer tok, String filename, int lineNo) { - int number_passes=0; - boolean finished = false; - - voidPointerExpansionToBoolean = false; - voidPointerExpansionToChar = false; - voidPointerExpansionToByte = false; - voidPointerExpansionToShort = false; - voidPointerExpansionToInt = false; - voidPointerExpansionToLong = false; - voidPointerExpansionToFloat = false; - voidPointerExpansionToDouble = false; - - while(!finished) { - try { - String mode = tok.nextToken(); - if (mode.equalsIgnoreCase("float")) { - voidPointerExpansionToFloat = true; - number_passes++; - } else if (mode.equalsIgnoreCase("int")) { - voidPointerExpansionToInt = true; - number_passes++; - } else if (mode.equalsIgnoreCase("byte")) { - voidPointerExpansionToByte = true; - number_passes++; - } else if (mode.equalsIgnoreCase("short")) { - voidPointerExpansionToShort = true; - number_passes++; - } else if (mode.equalsIgnoreCase("boolean")) { - voidPointerExpansionToBoolean = true; - number_passes++; - } else if (mode.equalsIgnoreCase("char")) { - voidPointerExpansionToChar = true; - number_passes++; - } else if (mode.equalsIgnoreCase("long")) { - voidPointerExpansionToLong = true; - number_passes++; - } else if (mode.equalsIgnoreCase("double")) { - voidPointerExpansionToDouble = true; - number_passes++; - } else { - throw new RuntimeException("Error parsing \"VoidPointerExpansion\" command at line " + lineNo + - " in file \"" + filename + - "\"; expected some combination of boolean, char, byte, short, int, long, float, double"); - } - } catch (NoSuchElementException e) { - if(number_passes == 0) { - throw new RuntimeException( - "Error parsing \"VoidPointerExpansion\" command at line " + lineNo + - " in file \"" + filename + "\"", e); - } - finished = true; - } - } - } - - - - /** - * When void* arguments in the C function prototypes are encountered, the - * emitter will try to expand the binding and create Java entry points for - * all possible array types. If there are 2 or more void* arguments in the C - * prototype, this directive lets you specify which of those arguments - * should always be expanded to the same type. <p> - * - * For example, given the C prototype: - * <pre> - * void FuncName(void *foo, void *bar); - * </pre> - * - * The emitter will normally emit multiple Java entry points: - * <pre> - * public abstract void FuncName(boolean[] foo, java.nio.Buffer bar); - * public abstract void FuncName(boolean[] foo, boolean[] bar); - * public abstract void FuncName(boolean[] foo, byte[] bar); - * public abstract void FuncName(boolean[] foo, char[] bar); - * public abstract void FuncName(boolean[] foo, short[] bar); - * public abstract void FuncName(boolean[] foo, int[] bar); - * public abstract void FuncName(boolean[] foo, long[] bar); - * public abstract void FuncName(boolean[] foo, float[] bar); - * public abstract void FuncName(boolean[] foo, double[] bar); - * - * public abstract void FuncName(byte[] foo, java.nio.Buffer bar); - * public abstract void FuncName(byte[] foo, boolean[] bar); - * public abstract void FuncName(byte[] foo, byte[] bar); - * <...etc for all variants on the second parameter...> - * - * public abstract void FuncName(char[] foo, java.nio.Buffer bar); - * public abstract void FuncName(char[] foo, boolean[] bar); - * public abstract void FuncName(char[] foo, byte[] bar); - * <...etc for all variants on the second parameter...> - * <...and so on for all remaining variants on the first parameter...> - * </pre> - * - * This directive lets you specify that arguments at a particular index - * should always be expanded to the same type. For example, the directive: - * <pre> - * MirrorExpandedBindingArgs FuncName 0 1 - * </pre> - * will force the first and second arguments in function FuncName to be - * expanded identically. This would result in the emission of the following - * entry points only: - * <pre> - * public abstract void FuncName(java.nio.Buffer[] foo, java.nio.Buffer bar); - * public abstract void FuncName(boolean[] foo, boolean[] bar); - * public abstract void FuncName(byte[] foo, byte[] bar); - * public abstract void FuncName(char[] foo, char[] bar); - * public abstract void FuncName(short[] foo, short[] bar); - * public abstract void FuncName(int[] foo, int[] bar); - * public abstract void FuncName(long[] foo, long[] bar); - * public abstract void FuncName(float[] foo, float[] bar); - * public abstract void FuncName(double[] foo, double[] bar); - * </pre> - */ - protected void readMirrorExpandedBindingArgs(StringTokenizer tok, String filename, int lineNo) { - try { - String methodName = tok.nextToken(); - ArrayList argIndices = new ArrayList(2); - while (tok.hasMoreTokens()) - { - Integer idx = Integer.valueOf(tok.nextToken()); - argIndices.add(idx); - } - - if(argIndices.size() > 1) - { - mirroredArgs.put(methodName, argIndices); - } - else - { - throw new RuntimeException("ERROR: Error parsing \"MirrorExpandedBindingArgs\" command at line " + lineNo + - " in file \"" + filename + "\": directive requires at least 2 argument indices"); - } - } catch (NoSuchElementException e) { - throw new RuntimeException( - "Error parsing \"MirrorExpandedBindingArgs\" command at line " + lineNo + - " in file \"" + filename + "\"", e); - } - } - /** * When const char* arguments in the C function prototypes are encountered, * the emitter will normally convert them to <code>byte[]</code> diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java index dad954db2..e260da992 100644 --- a/src/net/java/games/gluegen/JavaEmitter.java +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -300,170 +300,288 @@ public class JavaEmitter implements GlueEmitter { } /** + * Generates the public emitters for this MethodBinding which will + * produce either simply signatures (for the interface class, if + * any) or function definitions with or without a body (depending on + * whether or not the implementing function can go directly to + * native code because it doesn't need any processing of the + * outgoing arguments). + */ + protected void generatePublicEmitters(MethodBinding binding, + List allEmitters, + boolean signatureOnly) { + PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); + + if (cfg.manuallyImplement(binding.getName()) && !signatureOnly) { + // We only generate signatures for manually-implemented methods; + // user provides the implementation + return; + } + + // It's possible we may not need a body even if signatureOnly is + // set to false; for example, if the routine doesn't take any + // arrays or buffers as arguments + boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); + boolean needsBody = (isUnimplemented || + (binding.needsNIOWrappingOrUnwrapping() || + binding.signatureUsesJavaPrimitiveArrays())); + + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + !signatureOnly && needsBody, + false, + cfg.nioDirectOnly(binding.getName()), + false, + false, + false, + isUnimplemented); + emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); + if (cfg.allStatic()) { + emitter.addModifier(JavaMethodBindingEmitter.STATIC); + } + if (!isUnimplemented && !needsBody && !signatureOnly) { + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + } + emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(emitter); + } + + /** + * Generates the private emitters for this MethodBinding. On the + * Java side these will simply produce signatures for native + * methods. On the C side these will create the emitters which will + * write the JNI code to interface to the functions. We need to be + * careful to make the signatures all match up and not produce too + * many emitters which would lead to compilation errors from + * creating duplicated methods / functions. + */ + protected void generatePrivateEmitters(MethodBinding binding, + List allEmitters) { + if (cfg.manuallyImplement(binding.getName())) { + // Don't produce emitters for the implementation class + return; + } + + // If we already generated a public native entry point for this + // method, don't emit another one + if (!cfg.isUnimplemented(binding.getName()) && + (binding.needsNIOWrappingOrUnwrapping() || + binding.signatureUsesJavaPrimitiveArrays())) { + PrintWriter writer = (cfg.allStatic() ? javaWriter() : javaImplWriter()); + + // If the binding uses primitive arrays, we are going to emit + // the private native entry point for it along with the version + // taking only NIO buffers + if (!binding.signatureUsesJavaPrimitiveArrays()) { + // (Always) emit the entry point taking only direct buffers + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + false, + true, + cfg.nioDirectOnly(binding.getName()), + true, + true, + false, + false); + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + if (cfg.allStatic()) { + emitter.addModifier(JavaMethodBindingEmitter.STATIC); + } + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(emitter); + + // Optionally emit the entry point taking arrays which handles + // both the public entry point taking arrays as well as the + // indirect buffer case + if (!cfg.nioDirectOnly(binding.getName()) && + binding.signatureCanUseIndirectNIO()) { + emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + false, + true, + false, + true, + false, + true, + false); + + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + if (cfg.allStatic()) { + emitter.addModifier(JavaMethodBindingEmitter.STATIC); + } + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(emitter); + } + } + } + + // Now generate the C emitter(s). We need to produce one for every + // Java native entry point (public or private). The only + // situations where we don't produce one are (a) when the method + // is unimplemented, and (b) when the signature contains primitive + // arrays, since the latter is handled by the method binding + // variant taking only NIO Buffers. + if (!cfg.isUnimplemented(binding.getName()) && + !binding.signatureUsesJavaPrimitiveArrays()) { + // See whether we need an expression to help calculate the + // length of any return type + MessageFormat returnValueCapacityFormat = null; + MessageFormat returnValueLengthFormat = null; + JavaType javaReturnType = binding.getJavaReturnType(); + if (javaReturnType.isNIOBuffer() || + javaReturnType.isCompoundTypeWrapper()) { + // See whether capacity has been specified + String capacity = cfg.returnValueCapacity(binding.getName()); + if (capacity != null) { + returnValueCapacityFormat = new MessageFormat(capacity); + } + } else if (javaReturnType.isArray() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + // NOTE: adding a check here because the CMethodBindingEmitter + // also doesn't yet handle returning scalar arrays. In order + // to implement this, return the type as a Buffer instead + // (i.e., IntBuffer, FloatBuffer) and add code as necessary. + if (javaReturnType.isPrimitiveArray()) { + throw new RuntimeException("Primitive array return types not yet supported"); + } + + // See whether length has been specified + String len = cfg.returnValueLength(binding.getName()); + if (len != null) { + returnValueLengthFormat = new MessageFormat(len); + } + } + + CMethodBindingEmitter cEmitter = + new CMethodBindingEmitter(binding, + cWriter(), + cfg.implPackageName(), + cfg.implClassName(), + true, /* NOTE: we always disambiguate with a suffix now, so this is optional */ + cfg.allStatic(), + binding.needsNIOWrappingOrUnwrapping(), + false); + if (returnValueCapacityFormat != null) { + cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); + } + if (returnValueLengthFormat != null) { + cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); + } + cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); + cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); + allEmitters.add(cEmitter); + + // Now see if we have to emit another entry point to handle the + // indirect buffer and array case + if (binding.argumentsUseNIO() && + binding.signatureCanUseIndirectNIO() && + !cfg.nioDirectOnly(binding.getName())) { + cEmitter = + new CMethodBindingEmitter(binding, + cWriter(), + cfg.implPackageName(), + cfg.implClassName(), + true, /* NOTE: we always disambiguate with a suffix now, so this is optional */ + cfg.allStatic(), + binding.needsNIOWrappingOrUnwrapping(), + true); + if (returnValueCapacityFormat != null) { + cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); + } + if (returnValueLengthFormat != null) { + cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); + } + cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); + cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); + allEmitters.add(cEmitter); + } + } + } + + /** * Generate all appropriate Java bindings for the specified C function * symbols. */ protected List generateMethodBindingEmitters(FunctionSymbol sym) throws Exception { - ArrayList/*<FunctionEmitter>*/ allEmitters = new ArrayList(1); + ArrayList/*<FunctionEmitter>*/ allEmitters = new ArrayList(); try { // Get Java binding for the function MethodBinding mb = bindFunction(sym, null, null); - // Expand all void* arguments + // JavaTypes representing C pointers in the initial + // MethodBinding have not been lowered yet to concrete types List bindings = expandMethodBinding(mb); - boolean overloaded = (bindings.size() > 1); - if (overloaded) { - // resize ahead of time for speed - allEmitters.ensureCapacity(bindings.size()); - } - - // List of the indices of the arguments in this function that should be - // expanded to the same type when binding functions with multiple void* - // arguments - List mirrorIdxs = cfg.mirroredArgs(sym.getName()); for (Iterator iter = bindings.iterator(); iter.hasNext(); ) { MethodBinding binding = (MethodBinding) iter.next(); - // Honor the MirrorExpandedBindingArgs directive in .cfg files - if (mirrorIdxs != null) { - assert(mirrorIdxs.size() >= 2); // sanity check. - boolean typesMatch = true; - int argIndex = ((Integer)mirrorIdxs.get(0)).intValue(); - JavaType leftArgType = binding.getJavaArgumentType(argIndex); - for (int i = 1; i < mirrorIdxs.size(); ++i) { - argIndex = ((Integer)mirrorIdxs.get(i)).intValue(); - JavaType rightArgType = binding.getJavaArgumentType(argIndex); - if (!(leftArgType.equals(rightArgType))) { - typesMatch = false; - break; - } - leftArgType = rightArgType; - } - // Don't emit the binding if the specified args aren't the same type - if (!typesMatch) { continue; } // skip this binding - } - - // Try to create an NIOBuffer variant for this expanded binding. If - // it's the same as the original binding, then we'll be able to emit - // the binding like any normal binding because no special binding - // generation (wrapper methods, etc) will be necessary. - MethodBinding specialBinding = binding.createNIOBufferVariant(); - - if (cfg.allStatic() && binding.hasContainingType()) { // This should not currently happen since structs are emitted using a different mechanism throw new IllegalArgumentException("Cannot create binding in AllStatic mode because method has containing type: \"" + binding + "\""); } - boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); - JavaMethodBindingImplEmitter entryPoint=null; - - if (cfg.emitImpl()) { - // Generate the emitter for the method which may do conversion - // from type wrappers to NIO Buffers or which may call the - // underlying function directly - - boolean arrayImplMethod = false; - if(binding.signatureUsesPrimitiveArrays()) { - //overloaded = true; - arrayImplMethod = true; - } - - entryPoint = new JavaMethodBindingImplEmitter(binding, - (cfg.allStatic() ? javaWriter() : javaImplWriter()), - cfg.runtimeExceptionType(), - isUnimplemented, - arrayImplMethod); - entryPoint.addModifier(JavaMethodBindingEmitter.PUBLIC); - if (cfg.allStatic()) { - entryPoint.addModifier(JavaMethodBindingEmitter.STATIC); - } - if (!isUnimplemented && !bindingNeedsBody(binding)) { - entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); - } - entryPoint.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); - allEmitters.add(entryPoint); - } + // The structure of the generated glue code looks something like this: + // Simple method (no arrays, void pointers, etc.): + // Interface class: + // public void fooMethod(); + // Implementation class: + // public native void fooMethod(); + // + // Method taking void* argument: + // Interface class: + // public void fooMethod(Buffer arg); + // Implementation class: + // public void fooMethod(Buffer arg) { + // ... bounds checks, etc. ... + // if (arg.isDirect()) { + // fooMethod0(arg, computeDirectBufferByteOffset(arg)); + // } else { + // fooMethod1(getIndirectBufferArray(arg), computeIndirectBufferByteOffset(arg)); + // } + // } + // private native void fooMethod0(Object arg, int arg_byte_offset); + // private native void fooMethod1(Object arg, int arg_byte_offset); + // + // Method taking primitive array argument: + // Interface class: + // public void fooMethod(int[] arg, int arg_offset); + // public void fooMethod(IntBuffer arg); + // Implementing class: + // public void fooMethod(int[] arg, int arg_offset) { + // ... range checks, etc. ... + // fooMethod1(arg, SIZEOF_INT * arg_offset); + // } + // public void fooMethod(IntBuffer arg) { + // ... bounds checks, etc. ... + // if (arg.isDirect()) { + // fooMethod0(arg, computeDirectBufferByteOffset(arg)); + // } else { + // fooMethod1(getIndirectBufferArray(arg), computeIndirectBufferByteOffset(arg)); + // } + // } + // private native void fooMethod0(Object arg, int arg_byte_offset); + // private native void fooMethod1(Object arg, int arg_byte_offset); + // + // Note in particular that the public entry point taking an + // array is merely a special case of the indirect buffer case. if (cfg.emitInterface()) { - // Generate an emitter that will emit just the interface to the function - JavaMethodBindingEmitter entryPointInterface = - new JavaMethodBindingEmitter(binding, javaWriter(), cfg.runtimeExceptionType()); - entryPointInterface.addModifier(JavaMethodBindingEmitter.PUBLIC); - entryPointInterface.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); - allEmitters.add(entryPointInterface); + generatePublicEmitters(binding, allEmitters, true); } - if (cfg.emitImpl()) { - // If the user has stated that the function will be - // manually implemented, then don't auto-generate a function body. - if (!cfg.manuallyImplement(sym.getName()) && !isUnimplemented) { - // need to check if should create CMethodBindingImplEmitter instead of just - // CMethodBindingEmitter. Basically adds a "0" to JNI method name - boolean arrayImplMethod = false; - - if (bindingNeedsBody(binding)) { - // Generate the method which calls the underlying C function - // after unboxing has occurred - PrintWriter output = cfg.allStatic() ? javaWriter() : javaImplWriter(); - if(binding.signatureUsesPrimitiveArrays()) { - arrayImplMethod = true; - } - JavaMethodBindingEmitter wrappedEntryPoint = - new JavaMethodBindingEmitter(specialBinding, output, cfg.runtimeExceptionType(), true, - arrayImplMethod); - wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.PRIVATE); - wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.STATIC); // Doesn't really matter - wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); - allEmitters.add(wrappedEntryPoint); - - String bindingName = specialBinding.getName(); - if(binding != specialBinding && bindingName.contains("gl") && !bindingName.contains("glX") - && !bindingName.contains("wgl") && !bindingName.contains("CGL")) { - JavaMethodBindingEmitter wrappedEntryPoint2 = - new JavaMethodBindingEmitter(specialBinding, output, cfg.runtimeExceptionType(), - true, arrayImplMethod); - wrappedEntryPoint2.addModifier(JavaMethodBindingEmitter.PRIVATE); - wrappedEntryPoint2.addModifier(JavaMethodBindingEmitter.STATIC); // Doesn't really matter - wrappedEntryPoint2.addModifier(JavaMethodBindingEmitter.NATIVE); - - entryPoint.setGenerateIndirectBufferInterface(true); - wrappedEntryPoint2.setIndirectBufferInterface(true); - allEmitters.add(wrappedEntryPoint2); - } - - } - - CMethodBindingEmitter cEmitter = - makeCEmitter(specialBinding, - overloaded, - (binding != specialBinding), - arrayImplMethod, - cfg.implPackageName(), cfg.implClassName(), - cWriter()); - allEmitters.add(cEmitter); - - String bindingName = specialBinding.getName(); - if(binding != specialBinding && bindingName.contains("gl") && !bindingName.contains("glX") - && !bindingName.contains("wgl") && !bindingName.contains("CGL") ) { - - CMethodBindingEmitter cEmitter2 = - makeCEmitter(specialBinding, - //overloaded, - true, - true, - arrayImplMethod, - cfg.implPackageName(), cfg.implClassName(), - cWriter()); - cEmitter2.setIndirectBufferInterface(true); - allEmitters.add(cEmitter2); - } - - } + generatePublicEmitters(binding, allEmitters, false); + generatePrivateEmitters(binding, allEmitters); } } // end iteration over expanded bindings } catch (Exception e) { @@ -615,29 +733,49 @@ public class JavaEmitter implements GlueEmitter { FunctionSymbol funcSym = new FunctionSymbol(field.getName(), funcType); MethodBinding binding = bindFunction(funcSym, containingType, containingCType); binding.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis - MethodBinding specialBinding = binding.createNIOBufferVariant(); writer.println(); - JavaMethodBindingEmitter entryPoint = new JavaMethodBindingImplEmitter(binding, writer, cfg.runtimeExceptionType()); - entryPoint.addModifier(JavaMethodBindingEmitter.PUBLIC); - if (!bindingNeedsBody(binding) && !binding.hasContainingType()) { - entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); - } - entryPoint.emit(); - - JavaMethodBindingEmitter wrappedEntryPoint = new JavaMethodBindingEmitter(specialBinding, writer, cfg.runtimeExceptionType(), true, false); - wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.PRIVATE); - wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); - wrappedEntryPoint.emit(); - + // Emit public Java entry point for calling this function pointer + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + true, + false, + true, // FIXME: should unify this with the general emission code + false, + false, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false); + emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); + emitter.emit(); + + // Emit private native Java entry point for calling this function pointer + emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + false, + true, + true, // FIXME: should unify this with the general emission code + true, + true, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false); + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + emitter.emit(); + + // Emit (private) C entry point for calling this function pointer CMethodBindingEmitter cEmitter = - makeCEmitter(specialBinding, - false, // overloaded - true, // doing NIO impl routine? - false, // array impl method ? - structClassPkg, - containingTypeName, - cWriter); + new CMethodBindingEmitter(binding, + cWriter, + structClassPkg, + containingTypeName, + true, // FIXME: this is optional at this point + false, + true, + false); // FIXME: should unify this with the general emission code cEmitter.emit(); } catch (Exception e) { System.err.println("While processing field " + field + " of type " + name + ":"); @@ -726,59 +864,6 @@ public class JavaEmitter implements GlueEmitter { // Internals only below this point // - protected boolean bindingNeedsBody(MethodBinding binding) { - // We need to perform NIO checks and conversions and array length - // checks - return binding.signatureUsesNIO() || binding.signatureUsesCArrays() || binding.signatureUsesPrimitiveArrays(); - } - - private CMethodBindingEmitter makeCEmitter(MethodBinding binding, - boolean overloaded, - boolean doingNIOImplRoutine, - boolean doingArrayImplRoutine, - String bindingJavaPackageName, - String bindingJavaClassName, - PrintWriter output) { - MessageFormat returnValueCapacityFormat = null; - MessageFormat returnValueLengthFormat = null; - JavaType javaReturnType = binding.getJavaReturnType(); - if (javaReturnType.isNIOBuffer()) { - // See whether capacity has been specified - String capacity = cfg.returnValueCapacity(binding.getName()); - if (capacity != null) { - returnValueCapacityFormat = new MessageFormat(capacity); - } - } else if (javaReturnType.isArray()) { - // See whether length has been specified - String len = cfg.returnValueLength(binding.getName()); - if (len != null) { - returnValueLengthFormat = new MessageFormat(len); - } - } - CMethodBindingEmitter cEmitter; - if (doingNIOImplRoutine || doingArrayImplRoutine) { - cEmitter = new CMethodBindingImplEmitter(binding, overloaded, - doingArrayImplRoutine, - bindingJavaPackageName, - bindingJavaClassName, - cfg.allStatic(), output); - } else { - cEmitter = new CMethodBindingEmitter(binding, overloaded, - bindingJavaPackageName, - bindingJavaClassName, - cfg.allStatic(), output); - } - if (returnValueCapacityFormat != null) { - cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); - } - if (returnValueLengthFormat != null) { - cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); - } - cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); - cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); - return cEmitter; - } - private JavaType typeToJavaType(Type cType, boolean outgoingArgument) { // Recognize JNIEnv* case up front PointerType opt = cType.asPointer(); @@ -1233,6 +1318,12 @@ public class JavaEmitter implements GlueEmitter { return JavaType.createForClass(c); } + /** Maps the C types in the specified function to Java types through + the MethodBinding interface. Note that the JavaTypes in the + returned MethodBinding are "intermediate" JavaTypes (some + potentially representing C pointers rather than true Java types) + and must be lowered to concrete Java types before creating + emitters for them. */ private MethodBinding bindFunction(FunctionSymbol sym, JavaType containingType, Type containingCType) { @@ -1292,207 +1383,119 @@ public class JavaEmitter implements GlueEmitter { return binding; } - // Expands a MethodBinding containing C primitive pointer types into - // multiple variants taking Java primitive arrays and NIO buffers, subject - // to the per-function "NIO only" rule in the configuration file - private List/*<MethodBinding>*/ expandMethodBinding(MethodBinding binding) { - List result = new ArrayList(); - result.add(binding); - int i = 0; - while (i < result.size()) { - MethodBinding mb = (MethodBinding) result.get(i); - boolean shouldRemoveCurrent = false; - for (int j = 0; j < mb.getNumArguments(); j++) { - JavaType t = mb.getJavaArgumentType(j); - if (t.isCPrimitivePointerType()) { - // Remove original from list - shouldRemoveCurrent = true; - MethodBinding variant = null; - - // Non-NIO variants for non-void C primitive pointer types - if (!cfg.nioDirectOnly(mb.getCSymbol().getName()) && !t.isCVoidPointerType() - && !cfg.isPrimArrayExpModeNoPtrs()) { - if (t.isCCharPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.byteArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (t.isCShortPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.shortArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (t.isCInt32PointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.intArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (t.isCInt64PointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.longArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (t.isCFloatPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.floatArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (t.isCDoublePointerType()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.doubleArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - } - - // Non-NIO variants for void* C primitive pointer type - if (!cfg.nioDirectOnly(mb.getCSymbol().getName()) && t.isCVoidPointerType() - && cfg.isPrimArrayExpModeAllPtrs()) { - if (cfg.voidPointerExpansionToBoolean()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.booleanArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToChar()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.charArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToByte()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.byteArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToShort()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.shortArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToInt()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.intArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToLong()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.longArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToFloat()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.floatArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - if (cfg.voidPointerExpansionToDouble()) { - variant = mb.createCPrimitivePointerVariant(j, javaType(ArrayTypes.doubleArrayClass)); - if (! result.contains(variant)) result.add(variant); - } + private MethodBinding lowerMethodBindingPointerTypes(MethodBinding inputBinding, + boolean convertToArrays, + boolean[] canProduceArrayVariant) { + MethodBinding result = inputBinding; + boolean arrayPossible = false; + + for (int i = 0; i < inputBinding.getNumArguments(); i++) { + JavaType t = inputBinding.getJavaArgumentType(i); + if (t.isCPrimitivePointerType()) { + if (t.isCVoidPointerType()) { + // These are always bound to java.nio.Buffer + result = result.replaceJavaArgumentType(i, JavaType.forNIOBufferClass()); + } else if (t.isCCharPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.byteArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOByteBufferClass()); } - - - // NIO variant for void* C primitive pointer type - if (!cfg.noNio(mb.getCSymbol().getName())) { - if (t.isCVoidPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOBufferClass()); - if (! result.contains(variant)) result.add(variant); - } + } else if (t.isCShortPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.shortArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOShortBufferClass()); } - - - // NIO variants for non-void* C primitive pointer types - if ((cfg.nioMode() == JavaConfiguration.NIO_MODE_ALL_POINTERS && - !cfg.noNio(mb.getCSymbol().getName())) || - (cfg.nioMode() == JavaConfiguration.NIO_MODE_VOID_ONLY && - cfg.forcedNio(mb.getCSymbol().getName()))) { - if (t.isCCharPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOByteBufferClass()); - if (! result.contains(variant)) result.add(variant); - } - - if (t.isCShortPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOShortBufferClass()); - if (! result.contains(variant)) result.add(variant); - } - - if (t.isCInt32PointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOIntBufferClass()); - if (! result.contains(variant)) result.add(variant); - } - - if (t.isCInt64PointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOLongBufferClass()); - if (! result.contains(variant)) result.add(variant); - } - - if (t.isCFloatPointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIOFloatBufferClass()); - if (! result.contains(variant)) result.add(variant); - } - - if (t.isCDoublePointerType()) { - variant = mb.createCPrimitivePointerVariant(j, JavaType.forNIODoubleBufferClass()); - if (! result.contains(variant)) result.add(variant); - } + } else if (t.isCInt32PointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.intArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOIntBufferClass()); + } + } else if (t.isCInt64PointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.longArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOLongBufferClass()); + } + } else if (t.isCFloatPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.floatArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOFloatBufferClass()); + } + } else if (t.isCDoublePointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.doubleArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass()); } + } else { + throw new RuntimeException("Unknown C pointer type " + t); } } + } - if (mb.getJavaReturnType().isCPrimitivePointerType()) { - MethodBinding variant = null; - if (mb.getJavaReturnType().isCVoidPointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, JavaType.forNIOByteBufferClass()); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCCharPointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.byteArrayClass)); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCShortPointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.shortArrayClass)); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCInt32PointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.intArrayClass)); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCInt64PointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.longArrayClass)); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCFloatPointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.floatArrayClass)); - if (! result.contains(variant)) result.add(variant); - } else if (mb.getJavaReturnType().isCDoublePointerType()) { - variant = mb.createCPrimitivePointerVariant(-1, javaType(ArrayTypes.doubleArrayClass)); - if (! result.contains(variant)) result.add(variant); - } - shouldRemoveCurrent = true; - } - if (shouldRemoveCurrent) { - result.remove(i); - --i; + // Always return primitive pointer types as NIO buffers + JavaType t = result.getJavaReturnType(); + if (t.isCPrimitivePointerType()) { + if (t.isCVoidPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass()); + } else if (t.isCCharPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass()); + } else if (t.isCShortPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOShortBufferClass()); + } else if (t.isCInt32PointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOIntBufferClass()); + } else if (t.isCInt64PointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOLongBufferClass()); + } else if (t.isCFloatPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOFloatBufferClass()); + } else if (t.isCDoublePointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass()); + } else { + throw new RuntimeException("Unknown C pointer type " + t); } - ++i; - } - - // Honor the flattenNIOVariants directive in the configuration file - // FlattenNIOVariants <boolean> - // true: If there are multiple arguments in a method signature that map - // to NIO buffer, do not pair an NIO buffer argument with a primitive - // array argument - // false: Allow cross-pairing of nio and primitive array arguments in a - // single method signature. - if (cfg.flattenNIOVariants()) { - i = 0; - while (i < result.size()) { - boolean shouldRemoveCurrent = false; - MethodBinding mb = (MethodBinding) result.get(i); - for (int j = 0; j < binding.getNumArguments() && !shouldRemoveCurrent; j++) { - JavaType t1 = binding.getJavaArgumentType(j); - if (t1.isCPrimitivePointerType() && !t1.isCVoidPointerType()) { - for (int k = j + 1; k < binding.getNumArguments() && !shouldRemoveCurrent; k++) { - JavaType t2 = binding.getJavaArgumentType(k); - if (t2.isCPrimitivePointerType() && !t2.isCVoidPointerType()) { - // The "NIO-ness" of the converted arguments in the - // new binding must match - JavaType nt1 = mb.getJavaArgumentType(j); - JavaType nt2 = mb.getJavaArgumentType(k); - if (nt1.isNIOBuffer() != nt2.isNIOBuffer()) { - shouldRemoveCurrent = true; - } - } - } - } - } - if (shouldRemoveCurrent) { - result.remove(i); - --i; - } + } + + if (canProduceArrayVariant != null) { + canProduceArrayVariant[0] = arrayPossible; + } - ++i; + return result; + } + + // Expands a MethodBinding containing C primitive pointer types into + // multiple variants taking Java primitive arrays and NIO buffers, subject + // to the per-function "NIO only" rule in the configuration file + private List/*<MethodBinding>*/ expandMethodBinding(MethodBinding binding) { + List result = new ArrayList(); + // Indicates whether it is possible to produce an array variant + // Prevents e.g. char* -> String conversions from emitting two entry points + boolean[] canProduceArrayVariant = new boolean[1]; + + if (binding.signatureUsesCPrimitivePointers() || + binding.signatureUsesCVoidPointers() || + binding.signatureUsesCArrays()) { + result.add(lowerMethodBindingPointerTypes(binding, false, canProduceArrayVariant)); + + // FIXME: should add new configuration flag for this + if (canProduceArrayVariant[0] && + (binding.signatureUsesCPrimitivePointers() || + binding.signatureUsesCArrays()) && + !cfg.nioDirectOnly(binding.getName())) { + result.add(lowerMethodBindingPointerTypes(binding, true, null)); } + } else { + result.add(binding); } return result; diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java index 1d227dfc9..8c5cd2caf 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -39,8 +39,9 @@ package net.java.games.gluegen; -import java.util.*; import java.io.*; +import java.util.*; +import java.text.MessageFormat; import net.java.games.gluegen.cgram.types.*; import net.java.games.gluegen.cgram.*; @@ -58,20 +59,22 @@ public class JavaMethodBindingEmitter extends FunctionEmitter public static final EmissionModifier NATIVE = new EmissionModifier("native"); public static final EmissionModifier SYNCHRONIZED = new EmissionModifier("synchronized"); - protected static final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); - protected static final CommentEmitter defaultInterfaceCommentEmitter = + protected final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); + protected final CommentEmitter defaultInterfaceCommentEmitter = new InterfaceCommentEmitter(); // Exception type raised in the generated code if runtime checks fail private String runtimeExceptionType; - protected MethodBinding binding; + protected boolean emitBody; + protected boolean eraseBufferAndArrayTypes; + protected boolean directNIOOnly; protected boolean forImplementingMethodCall; - protected boolean forArrayImplementingMethodCall = false; - - protected boolean prefixedMethod = false; + protected boolean forDirectBufferImplementation; + protected boolean forIndirectBufferAndArrayImplementation; + protected boolean isUnimplemented; - protected boolean indirectBufferInterface = false; + protected MethodBinding binding; // A non-null value indicates that rather than returning a compound // type accessor we are returning an array of such accessors; this @@ -80,26 +83,45 @@ public class JavaMethodBindingEmitter extends FunctionEmitter // number of elements of the returned array. private String returnedArrayLengthExpression; - public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType) - { - this(binding, output, runtimeExceptionType, false, false); - } - - public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType, boolean forImplementingMethodCall, boolean forArrayImplementingMethodCall) + public JavaMethodBindingEmitter(MethodBinding binding, + PrintWriter output, + String runtimeExceptionType, + boolean emitBody, + boolean eraseBufferAndArrayTypes, + boolean directNIOOnly, + boolean forImplementingMethodCall, + boolean forDirectBufferImplementation, + boolean forIndirectBufferAndArrayImplementation, + boolean isUnimplemented) { super(output); this.binding = binding; - this.forImplementingMethodCall = forImplementingMethodCall; - this.forArrayImplementingMethodCall = forArrayImplementingMethodCall; this.runtimeExceptionType = runtimeExceptionType; - setCommentEmitter(defaultInterfaceCommentEmitter); + this.emitBody = emitBody; + this.eraseBufferAndArrayTypes = eraseBufferAndArrayTypes; + this.directNIOOnly = directNIOOnly; + this.forImplementingMethodCall = forImplementingMethodCall; + this.forDirectBufferImplementation = forDirectBufferImplementation; + this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; + this.isUnimplemented = isUnimplemented; + if (forImplementingMethodCall) { + setCommentEmitter(defaultJavaCommentEmitter); + } else { + setCommentEmitter(defaultInterfaceCommentEmitter); + } } public JavaMethodBindingEmitter(JavaMethodBindingEmitter arg) { super(arg); - runtimeExceptionType = arg.runtimeExceptionType; binding = arg.binding; + runtimeExceptionType = arg.runtimeExceptionType; + emitBody = arg.emitBody; + eraseBufferAndArrayTypes = arg.eraseBufferAndArrayTypes; + directNIOOnly = arg.directNIOOnly; forImplementingMethodCall = arg.forImplementingMethodCall; + forDirectBufferImplementation = arg.forDirectBufferImplementation; + forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation; + isUnimplemented = arg.isUnimplemented; returnedArrayLengthExpression = arg.returnedArrayLengthExpression; } @@ -111,16 +133,6 @@ public class JavaMethodBindingEmitter extends FunctionEmitter return binding.getName(); } - public boolean isIndirectBufferInterface() { - return indirectBufferInterface; - } - - - public void setIndirectBufferInterface(boolean indirect) { - indirectBufferInterface = indirect; - } - - /** The type of exception (must subclass <code>java.lang.RuntimeException</code>) raised if runtime checks fail in the generated code. */ @@ -139,23 +151,78 @@ public class JavaMethodBindingEmitter extends FunctionEmitter returnedArrayLengthExpression = expr; } + /** Indicates whether this emitter will print only a signature, or + whether it will emit Java code for the body of the method as + well. */ + public boolean signatureOnly() { + return !emitBody; + } + + /** Accessor for subclasses. */ + public void setEmitBody(boolean emitBody) { + this.emitBody = emitBody; + } + + /** Accessor for subclasses. */ + public void setEraseBufferAndArrayTypes(boolean erase) { + this.eraseBufferAndArrayTypes = erase; + } + + /** Accessor for subclasses. */ + public void setForImplementingMethodCall(boolean impl) { + this.forImplementingMethodCall = impl; + } + protected void emitReturnType(PrintWriter writer) { writer.print(getReturnTypeString(false)); } + protected String erasedTypeString(JavaType type, boolean skipBuffers) { + if (eraseBufferAndArrayTypes) { + if (type.isNIOBuffer() || + type.isPrimitiveArray()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Object"; + } + } else if (type.isCompoundTypeWrapper()) { + // Compound type wrappers are unwrapped to ByteBuffer + return "java.nio.ByteBuffer"; + } else if (type.isArrayOfCompoundTypeWrappers()) { + return "java.nio.ByteBuffer"; + } + } + return type.getName(); + } + protected String getReturnTypeString(boolean skipArray) { - if (skipArray || (getReturnedArrayLengthExpression() == null && - !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers())) { - return binding.getJavaReturnType().getName(); + // The first arm of the "if" clause is used by the glue code + // generation for arrays of compound type wrappers + if (skipArray || + // The following arm is used by most other kinds of return types + (getReturnedArrayLengthExpression() == null && + !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers()) || + // The following arm is used specifically to get the splitting up + // of one returned ByteBuffer into an array of compound type + // wrappers to work (e.g., XGetVisualInfo) + (eraseBufferAndArrayTypes && + binding.getJavaReturnType().isCompoundTypeWrapper() && + (getReturnedArrayLengthExpression() != null))) { + return erasedTypeString(binding.getJavaReturnType(), true); } - return binding.getJavaReturnType().getName() + "[]"; + return erasedTypeString(binding.getJavaReturnType(), true) + "[]"; } protected void emitName(PrintWriter writer) { if (forImplementingMethodCall) { - writer.print(getImplMethodName()); + if (forIndirectBufferAndArrayImplementation) { + writer.print(getImplMethodName(false)); + } else { + writer.print(getImplMethodName(true)); + } } else { writer.print(getName()); } @@ -165,16 +232,13 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { boolean needComma = false; int numEmitted = 0; - int numBufferOffsetArgs = 0, numBufferOffsetArrayArgs = 0; if (forImplementingMethodCall && binding.hasContainingType()) { // Always emit outgoing "this" argument - writer.print("java.nio.Buffer "); + writer.print("java.nio.ByteBuffer "); writer.print(javaThisArgumentName()); ++numEmitted; needComma = true; - numBufferOffsetArgs++; - writer.print(", int " + byteOffsetConversionArgName(numBufferOffsetArgs)); } for (int i = 0; i < binding.getNumArguments(); i++) { @@ -199,69 +263,354 @@ public class JavaMethodBindingEmitter extends FunctionEmitter writer.print(", "); } - // indirect buffer array sent down as object - if(isIndirectBufferInterface() && type.isNIOBuffer() ) { - writer.print(" Object "); - } else { - writer.print(type.getName()); - } - + writer.print(erasedTypeString(type, false)); writer.print(" "); writer.print(binding.getArgumentName(i)); - ++numEmitted; needComma = true; - // Add Buffer offset argument to store the buffer offset - if((forImplementingMethodCall || prefixedMethod) && - (type.isNIOBuffer() || type.isNIOBufferArray())) { - if(!type.isArray()) { - numBufferOffsetArgs++; - writer.print(", int " + byteOffsetConversionArgName(numBufferOffsetArgs)); - } else { - numBufferOffsetArrayArgs++; - writer.print(", int[] " + - byteOffsetArrayConversionArgName(numBufferOffsetArrayArgs)); - } + // Add Buffer and array index offset arguments after each associated argument + if (forDirectBufferImplementation || forIndirectBufferAndArrayImplementation) { + if (type.isNIOBuffer()) { + writer.print(", int " + byteOffsetArgName(i)); + } else if (type.isNIOBufferArray()) { + writer.print(", int[] " + + byteOffsetArrayArgName(i)); + } } - // Add array index offset argument after each primitive array - if( type.isArray() && !type.isNIOBufferArray() && !type.isStringArray()) { - writer.print(", int " + binding.getArgumentName(i) + "_offset"); + // Add offset argument after each primitive array + if (type.isPrimitiveArray()) { + writer.print(", int " + offsetArgName(i)); } - } return numEmitted; } - protected String getImplMethodName() - { - if( isIndirectBufferInterface() ) - return binding.getName() + "2"; - else if(!forArrayImplementingMethodCall) - return binding.getName() + "0"; - else - return binding.getName() + "1"; + protected String getImplMethodName(boolean direct) { + if (direct) { + return binding.getName() + "0"; + } else { + return binding.getName() + "1"; + } } + protected String byteOffsetArgName(int i) { + return byteOffsetArgName(binding.getArgumentName(i)); + } - protected String byteOffsetConversionArgName(int i) { - return "__byteOffset" + i; + protected String byteOffsetArgName(String s) { + return s + "_byte_offset"; } - protected String byteOffsetArrayConversionArgName(int i) { - return "__byteOffsetArray" + i; + protected String byteOffsetArrayArgName(int i) { + return binding.getArgumentName(i) + "_byte_offset_array"; } + protected String offsetArgName(int i) { + return binding.getArgumentName(i) + "_offset"; + } protected void emitBody(PrintWriter writer) { - writer.println(';'); + if (!emitBody) { + writer.println(';'); + } else { + MethodBinding binding = getBinding(); + writer.println(); + writer.println(" {"); + if (isUnimplemented) { + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Unimplemented\");"); + } else { + emitPreCallSetup(binding, writer); + //emitReturnVariableSetup(binding, writer); + emitReturnVariableSetupAndCall(binding, writer); + } + writer.println(" }"); + } } - protected static String javaThisArgumentName() { + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + emitArrayLengthAndNIOBufferChecks(binding, writer); + } + + protected void emitArrayLengthAndNIOBufferChecks(MethodBinding binding, PrintWriter writer) { + int numBufferOffsetArrayArgs = 0; + boolean firstBuffer = true; + // Check lengths of any incoming arrays if necessary + for (int i = 0; i < binding.getNumArguments(); i++) { + Type type = binding.getCArgumentType(i); + if (type.isArray()) { + ArrayType arrayType = type.asArray(); + writer.println(" if (" + binding.getArgumentName(i) + ".length < " + + arrayType.getLength() + ")"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Length of array \\\"" + binding.getArgumentName(i) + + "\\\" was less than the required " + arrayType.getLength() + "\");"); + } else { + JavaType javaType = binding.getJavaArgumentType(i); + if (javaType.isNIOBuffer()) { + if (directNIOOnly) { + writer.println(" if (!BufferFactory.isDirect(" + binding.getArgumentName(i) + "))"); + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Argument \\\"" + + binding.getArgumentName(i) + "\\\" was not a direct buffer\");"); + } else { + if(firstBuffer) { + firstBuffer = false; + writer.println(" boolean _direct = BufferFactory.isDirect(" + binding.getArgumentName(i) + ");"); + } else { + writer.println(" if (_direct != BufferFactory.isDirect(" + binding.getArgumentName(i) + "))"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Argument \\\"" + binding.getArgumentName(i) + + "\\\" : Buffers passed to this method must all be either direct or indirect\");"); + } + } + } else if (javaType.isNIOBufferArray()) { + // All buffers passed down in an array of NIO buffers must be direct + String argName = binding.getArgumentName(i); + String arrayName = byteOffsetArrayArgName(i); + writer.println(" int[] " + arrayName + " = new int[" + argName + ".length];"); + // Check direct buffer properties of all buffers within + writer.println(" if (" + argName + " != null) {"); + writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); + writer.println(" if (!BufferFactory.isDirect(" + argName + "[_ctr])) {"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Element \" + _ctr + \" of argument \\\"" + + binding.getArgumentName(i) + "\\\" was not a direct buffer\");"); + writer.println(" }"); + // get the Buffer Array offset values and save them into another array to send down to JNI + writer.print (" " + arrayName + "[_ctr] = BufferFactory.getDirectBufferByteOffset("); + writer.println(argName + "[_ctr]);"); + writer.println(" }"); + writer.println(" }"); + } else if (javaType.isPrimitiveArray()) { + String argName = binding.getArgumentName(i); + String offsetArg = offsetArgName(i); + writer.println(" if(" + argName + " != null && " + argName + ".length <= " + offsetArg + ")"); + writer.print (" throw new " + getRuntimeExceptionType()); + writer.println("(\"array offset argument \\\"" + offsetArg + "\\\" (\" + " + offsetArg + + " + \") equals or exceeds array length (\" + " + argName + ".length + \")\");"); + } + } + } + } + + protected void emitCall(MethodBinding binding, PrintWriter writer, boolean direct) { + writer.print(getImplMethodName(direct)); + writer.print("("); + emitCallArguments(binding, writer, direct); + writer.print(");"); + } + + + protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { + writer.print(" "); + JavaType returnType = binding.getJavaReturnType(); + boolean needsResultAssignment = false; + + if (!returnType.isVoid()) { + if (returnType.isCompoundTypeWrapper() || + returnType.isNIOByteBuffer()) { + writer.println("ByteBuffer _res;"); + needsResultAssignment = true; + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("ByteBuffer[] _res;"); + needsResultAssignment = true; + } + } + + if (binding.signatureCanUseIndirectNIO() && !directNIOOnly) { + // Must generate two calls for this gated on whether the NIO + // buffers coming in are all direct or indirect + writer.println("if (_direct) {"); + writer.print (" "); + } + + if (needsResultAssignment) { + writer.print(" _res = "); + } else { + writer.print(" "); + if (!returnType.isVoid()) { + writer.print("return "); + } + } + + if (binding.signatureUsesJavaPrimitiveArrays() && + !binding.signatureCanUseIndirectNIO()) { + // FIXME: what happens with a C function of the form + // void foo(int* arg0, void* arg1); + // ? + + // Only one call being made in this body, going to indirect + // buffer / array entry point + emitCall(binding, writer, false); + writer.println(); + } else { + emitCall(binding, writer, true); + } + + if (binding.signatureCanUseIndirectNIO() && !directNIOOnly) { + // Must generate two calls for this gated on whether the NIO + // buffers coming in are all direct or indirect + writer.println(); + writer.println(" } else {"); + writer.print (" "); + if (needsResultAssignment) { + writer.print(" _res = "); + } else { + writer.print(" "); + if (!returnType.isVoid()) { + writer.print("return "); + } + } + emitCall(binding, writer, false); + writer.println(); + writer.println(" }"); + } else { + writer.println(); + } + if (needsResultAssignment) { + emitCallResultReturn(binding, writer); + } + } + + protected int emitCallArguments(MethodBinding binding, PrintWriter writer, boolean direct) { + boolean needComma = false; + int numArgsEmitted = 0; + + if (binding.hasContainingType()) { + // Emit this pointer + assert(binding.getContainingType().isCompoundTypeWrapper()); + writer.print("getBuffer()"); + needComma = true; + ++numArgsEmitted; + } + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(binding.getNumArguments() == 1); + continue; + } + + if (needComma) { + writer.print(", "); + } + + if (type.isCompoundTypeWrapper()) { + writer.print("(("); + } + + if (type.isNIOBuffer() && !direct) { + writer.print("BufferFactory.getArray(" + binding.getArgumentName(i) + ")"); + } else { + writer.print(binding.getArgumentName(i)); + } + + if (type.isCompoundTypeWrapper()) { + writer.print(" == null) ? null : "); + writer.print(binding.getArgumentName(i)); + writer.print(".getBuffer())"); + } + needComma = true; + ++numArgsEmitted; + if (type.isNIOBuffer()) { + if (direct) { + writer.print(", BufferFactory.getDirectBufferByteOffset(" + binding.getArgumentName(i) + ")"); + } else { + writer.print(", BufferFactory.getIndirectBufferByteOffset(" + binding.getArgumentName(i) + ")"); + } + } else if (type.isNIOBufferArray()) { + writer.print(", " + byteOffsetArrayArgName(i)); + } + + // Add Array offset parameter for primitive arrays + if (type.isPrimitiveArray()) { + if(type.isFloatArray()) { + writer.print(", BufferFactory.SIZEOF_FLOAT * "); + } else if(type.isDoubleArray()) { + writer.print(", BufferFactory.SIZEOF_DOUBLE * "); + } else if(type.isByteArray()) { + writer.print(", "); + } else if(type.isLongArray()) { + writer.print(", BufferFactory.SIZEOF_LONG * "); + } else if(type.isShortArray()) { + writer.print(", BufferFactory.SIZEOF_SHORT * "); + } else if(type.isIntArray()) { + writer.print(", BufferFactory.SIZEOF_INT * "); + } else { + throw new RuntimeException("Unsupported type for calculating array offset argument for " + + binding.getArgumentName(i) + + "-- error occurred while processing Java glue code for " + binding.getName()); + } + writer.print(offsetArgName(i)); + } + } + return numArgsEmitted; + } + + protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { + JavaType returnType = binding.getJavaReturnType(); + + if (returnType.isCompoundTypeWrapper()) { + String fmt = getReturnedArrayLengthExpression(); + writer.println(" if (_res == null) return null;"); + if (fmt == null) { + writer.print(" return new " + returnType.getName() + "(_res.order(ByteOrder.nativeOrder()))"); + } else { + writer.println(" _res.order(ByteOrder.nativeOrder());"); + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + String expr = new MessageFormat(fmt).format(argumentNames); + PointerType cReturnTypePointer = binding.getCReturnType().asPointer(); + CompoundType cReturnType = null; + if (cReturnTypePointer != null) { + cReturnType = cReturnTypePointer.getTargetType().asCompound(); + } + if (cReturnType == null) { + throw new RuntimeException("ReturnedArrayLength directive currently only supported for pointers to compound types " + + "(error occurred while generating Java glue code for " + binding.getName() + ")"); + } + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[" + expr + "];"); + writer.println(" for (int _count = 0; _count < " + expr + "; _count++) {"); + // Create temporary ByteBuffer slice + // FIXME: probably need Type.getAlignedSize() for arrays of + // compound types (rounding up to machine-dependent alignment) + writer.println(" _res.position(_count * " + cReturnType.getSize() + ");"); + writer.println(" _res.limit ((1 + _count) * " + cReturnType.getSize() + ");"); + writer.println(" ByteBuffer _tmp = _res.slice();"); + writer.println(" _tmp.order(ByteOrder.nativeOrder());"); + writer.println(" _res.position(0);"); + writer.println(" _res.limit(_res.capacity());"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_tmp);"); + writer.println(" }"); + writer.print (" return _retarray"); + } + writer.println(";"); + } else if (returnType.isNIOBuffer()) { + writer.println(" if (_res == null) return null;"); + writer.println(" return _res.order(ByteOrder.nativeOrder());"); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println(" if (_res == null) return null;"); + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); + writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_res[_count]);"); + writer.println(" }"); + writer.println(" return _retarray;"); + } + } + + public static String javaThisArgumentName() { return "jthis0"; } @@ -278,7 +627,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter * includes the C signature of the native method that is being bound by the * emitter java method. */ - protected static class DefaultCommentEmitter implements CommentEmitter { + protected class DefaultCommentEmitter implements CommentEmitter { public void emit(FunctionEmitter emitter, PrintWriter writer) { emitBeginning(emitter, writer); emitBindingCSignature(((JavaMethodBindingEmitter)emitter).getBinding(), writer); @@ -295,9 +644,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter protected void emitEnding(FunctionEmitter emitter, PrintWriter writer) { // If argument type is a named enum, then emit a comment detailing the // acceptable values of that enum. + // If we're emitting a direct buffer variant only, then declare + // that the NIO buffer arguments must be direct. MethodBinding binding = ((JavaMethodBindingEmitter)emitter).getBinding(); for (int i = 0; i < binding.getNumArguments(); i++) { Type type = binding.getCArgumentType(i); + JavaType javaType = binding.getJavaArgumentType(i); // don't emit param comments for anonymous enums, since we can't // distinguish between the values found within multiple anonymous // enums in the same C translation unit. @@ -314,12 +666,19 @@ public class JavaMethodBindingEmitter extends FunctionEmitter writer.print(enumType.getEnumName(j)); } writer.println("</code>"); + } else if (directNIOOnly && javaType.isNIOBuffer()) { + writer.println(); + writer.print(emitter.getBaseIndentString()); + writer.print(" "); + writer.print("@param "); + writer.print(binding.getArgumentName(i)); + writer.print(" a direct {@link " + javaType.getName() + "}"); } } } } - protected static class InterfaceCommentEmitter + protected class InterfaceCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { protected void emitBeginning(FunctionEmitter emitter, diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java deleted file mode 100644 index 3cbc8081d..000000000 --- a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package net.java.games.gluegen; - -import java.io.*; -import java.util.*; -import java.text.MessageFormat; - -import net.java.games.gluegen.cgram.types.*; -import net.java.games.jogl.util.BufferUtils; - -/** Emits the Java-side component of the Java<->C JNI binding. */ -public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter -{ - private boolean isUnimplemented; - - public JavaMethodBindingImplEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType) - { - this(binding, output, runtimeExceptionType, false, false); - } - - public JavaMethodBindingImplEmitter(MethodBinding binding, - PrintWriter output, - String runtimeExceptionType, - boolean isUnimplemented, - boolean arrayImplExpansion) - { - super(binding, output, runtimeExceptionType); - setCommentEmitter(defaultJavaCommentEmitter); - this.isUnimplemented = isUnimplemented; - this.forArrayImplementingMethodCall = arrayImplExpansion; - } - - public JavaMethodBindingImplEmitter(JavaMethodBindingEmitter arg) { - super(arg); - if (arg instanceof JavaMethodBindingImplEmitter) { - this.isUnimplemented = ((JavaMethodBindingImplEmitter) arg).isUnimplemented; - } - } - - protected void emitBody(PrintWriter writer) - { - MethodBinding binding = getBinding(); - if (needsBody()) { - writer.println(); - writer.println(" {"); - if (isUnimplemented) { - writer.println(" throw new " + getRuntimeExceptionType() + "(\"Unimplemented\");"); - } else { - emitPreCallSetup(binding, writer); - //emitReturnVariableSetup(binding, writer); - emitReturnVariableSetupAndCall(binding, writer); - } - writer.println(" }"); - } else { - writer.println(";"); - } - } - - - protected boolean generateIndirectBufferInterface = false; - - public boolean isGenerateIndirectBufferInterface() { - return generateIndirectBufferInterface; - } - - public void setGenerateIndirectBufferInterface(boolean generateIndirect) { - generateIndirectBufferInterface = generateIndirect; - } - - - protected boolean isUnimplemented() { - return isUnimplemented; - } - - protected boolean needsBody() { - return (isUnimplemented || - getBinding().signatureUsesNIO() || - getBinding().signatureUsesCArrays() || - getBinding().signatureUsesPrimitiveArrays() || - getBinding().hasContainingType()); - } - - protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { - if(isGenerateIndirectBufferInterface()) { - // case for when indirect Buffer is a possibility - emitArrayLengthAndNIOInDirectBufferChecks(binding, writer); - } else { - emitArrayLengthAndNIOBufferChecks(binding, writer); - } - } - - - protected void emitArrayLengthAndNIOBufferChecks(MethodBinding binding, PrintWriter writer) { - int numBufferOffsetArrayArgs = 0; - // Check lengths of any incoming arrays if necessary - for (int i = 0; i < binding.getNumArguments(); i++) { - Type type = binding.getCArgumentType(i); - JavaType javaType = binding.getJavaArgumentType(i); - if (type.isArray()) { - ArrayType arrayType = type.asArray(); - writer.println(" if (" + binding.getArgumentName(i) + ".length < " + arrayType.getLength() + ")"); - writer.println(" throw new " + getRuntimeExceptionType() + "(\"Length of array \\\"" + binding.getArgumentName(i) + - "\\\" was less than the required " + arrayType.getLength() + "\");"); - } else { - if (javaType.isNIOBuffer()) { - writer.println(" if (!BufferFactory.isDirect(" + binding.getArgumentName(i) + "))"); - writer.println(" throw new " + getRuntimeExceptionType() + "(\"Argument \\\"" + - binding.getArgumentName(i) + "\\\" was not a direct buffer\");"); - } else if (javaType.isNIOBufferArray()) { - numBufferOffsetArrayArgs++; - String argName = binding.getArgumentName(i); - String arrayName = byteOffsetArrayConversionArgName(numBufferOffsetArrayArgs); - writer.println(" int[] " + arrayName + " = new int[" + argName + ".length];"); - // Check direct buffer properties of all buffers within - writer.println(" if (" + argName + " != null) {"); - writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); - writer.println(" if (!BufferFactory.isDirect(" + argName + "[_ctr])) {"); - writer.println(" throw new " + getRuntimeExceptionType() + - "(\"Element \" + _ctr + \" of argument \\\"" + - binding.getArgumentName(i) + "\\\" was not a direct buffer\");"); - writer.println(" }"); - // get the Buffer Array offset values and save them into another array to send down to JNI - writer.print(" " + arrayName + "[_ctr] = BufferFactory.getDirectBufferByteOffset("); - writer.println(argName + "[_ctr]);"); - writer.println(" }"); - writer.println(" }"); - } else if (javaType.isArray() && !javaType.isNIOBufferArray() &&!javaType.isStringArray()) { - String argName = binding.getArgumentName(i); - String offsetArg = argName + "_offset"; - writer.println(" if(" + argName + " != null && " + argName + ".length <= " + offsetArg + ")"); - writer.print(" throw new " + getRuntimeExceptionType()); - writer.println("(\"array offset argument \\\"" + offsetArg + "\\\" equals or exceeds array length\");"); - } - } - } - } - - - protected void emitArrayLengthAndNIOInDirectBufferChecks(MethodBinding binding, PrintWriter writer) { - int numBufferOffsetArrayArgs = 0; - boolean firstBuffer = true; - // Check lengths of any incoming arrays if necessary - for (int i = 0; i < binding.getNumArguments(); i++) { - Type type = binding.getCArgumentType(i); - if (type.isArray()) { - ArrayType arrayType = type.asArray(); - writer.println(" if (" + binding.getArgumentName(i) + ".length < " + - arrayType.getLength() + ")"); - writer.println(" throw new " + getRuntimeExceptionType() + - "(\"Length of array \\\"" + binding.getArgumentName(i) + - "\\\" was less than the required " + arrayType.getLength() + "\");"); - } else { - JavaType javaType = binding.getJavaArgumentType(i); - if (javaType.isNIOBuffer()) { - if(firstBuffer == true) { - firstBuffer = false; - writer.println(" boolean direct = true, firstTime = true, result = true;"); - } - writer.println(" result = BufferFactory.isDirect(" + binding.getArgumentName(i) + ");"); - writer.println(" if(firstTime == true) {"); - writer.println(" direct = result;"); - writer.println(" firstTime = false;"); - writer.println(" } else {"); - writer.println(" if(direct != result)"); - writer.println(" throw new " + getRuntimeExceptionType() + - "(\"Argument \\\"" + binding.getArgumentName(i) + - "\\\" :Not all Buffers in this method were direct or indirect\");"); - writer.println(" }"); - } else if (javaType.isNIOBufferArray()) { - if(firstBuffer == true) { - firstBuffer = false; - writer.println(" boolean direct = true, firstTime = true, result = true;"); - } - numBufferOffsetArrayArgs++; - String argName = binding.getArgumentName(i); - String arrayName = byteOffsetArrayConversionArgName(numBufferOffsetArrayArgs); - writer.println(" int[] " + arrayName + " = new int[" + argName + ".length];"); - writer.println(" if (" + argName + " != null) {"); - // Check direct/indirect buffer properties of all buffers within - writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); - writer.println(" result = BufferFactory.isDirect(" + argName + "[_ctr]);"); - - writer.println(" if(firstTime == true) {"); - writer.println(" direct = result;"); - writer.println(" firstTime = false;"); - writer.println(" } else {"); - writer.println(" if(direct != result)"); - writer.println(" throw new " + getRuntimeExceptionType() + "(\"Element \" + _ctr + \" of argument \\\"" + binding.getArgumentName(i) + "\\\":Mixture of Direct/Indirect Buffers in Method Args\");"); - writer.println(" }"); - // get the Buffer Array offset values and save them into another array to send down to JNI - writer.println(" if(direct)"); - writer.print(" " + arrayName + "[_ctr] = BufferFactory.getDirectBufferByteOffset("); - writer.println(argName + "[_ctr]);"); - writer.println(" else"); - writer.print(" " + arrayName + "[_ctr] = BufferFactory.getIndirectBufferByteOffset("); - writer.println(argName + "[_ctr]);"); - writer.println(" }"); - writer.println(" }"); - } - } - } - } - - - -/* old method before indirect buffer support was added - protected void emitReturnVariableSetup(MethodBinding binding, PrintWriter writer) { - writer.print(" "); - JavaType returnType = binding.getJavaReturnType(); - if (!returnType.isVoid()) { - if (returnType.isCompoundTypeWrapper() || - returnType.isNIOByteBuffer()) { - writer.println("ByteBuffer _res;"); - writer.print(" _res = "); - } else if (returnType.isArrayOfCompoundTypeWrappers()) { - writer.println("ByteBuffer[] _res;"); - writer.print(" _res = "); - } else { - writer.print("return "); - } - } - } -*/ - - - protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { - - boolean returnFunction = false; - - writer.print(" "); - JavaType returnType = binding.getJavaReturnType(); - if (!returnType.isVoid()) { - if (returnType.isCompoundTypeWrapper() || - returnType.isNIOByteBuffer()) { - writer.println("ByteBuffer _res;"); - writer.print(" _res = "); - } else if (returnType.isArrayOfCompoundTypeWrappers()) { - writer.println("ByteBuffer[] _res;"); - writer.print(" _res = "); - } else { - if(isGenerateIndirectBufferInterface()) - returnFunction = true; - else - writer.print("return "); - } - } - - - if(isGenerateIndirectBufferInterface()) { - //binding.setIndirectVariant(false); - //writer.println(" boolean direct = true;"); - writer.println(" if(direct) {"); - writer.print(" "); - }; - if(returnFunction) writer.print("return "); - writer.print(getImplMethodName()); - writer.print("("); - emitCallArguments(binding, writer, false); - writer.print(")"); - if(isGenerateIndirectBufferInterface()) { - writer.println(";"); - //binding.setIndirectVariant(true); - writer.println(" } else { "); - if(returnFunction) writer.print("return "); - // get the indirect Buffer implementation name - setIndirectBufferInterface(true); - writer.print(" " + getImplMethodName()); - writer.print("("); - setIndirectBufferInterface(false); - emitCallArguments(binding, writer, true); - writer.println(");"); - writer.println(" }"); - }; - emitCallResultReturn(binding, writer); - } - - - protected int emitCallArguments(MethodBinding binding, PrintWriter writer, boolean indirectCase) { - boolean needComma = false; - int numArgsEmitted = 0; - int numBufferOffsetArgs = 0, numBufferOffsetArrayArgs = 0; - boolean generateDirectAndIndirect; - - generateDirectAndIndirect = isGenerateIndirectBufferInterface(); - - if (binding.hasContainingType()) { - // Emit this pointer - assert(binding.getContainingType().isCompoundTypeWrapper()); - writer.print("getBuffer()"); - needComma = true; - ++numArgsEmitted; - numBufferOffsetArgs++; - //writer.print(", " + byteOffsetConversionArgName(numBufferOffsetArgs)); - writer.print(", BufferFactory.getDirectBufferByteOffset(getBuffer())"); - } - for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); - if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { - // Don't need to expose these at the Java level - continue; - } - - if (type.isVoid()) { - // Make sure this is the only param to the method; if it isn't, - // there's something wrong with our parsing of the headers. - assert(binding.getNumArguments() == 1); - continue; - } - - if (needComma) { - writer.print(", "); - } - - if (type.isCompoundTypeWrapper()) { - writer.print("(("); - } - - if(type.isNIOBuffer() && !type.isNIOBufferArray() && generateDirectAndIndirect && indirectCase) { - writer.print("BufferFactory.getArray(" + binding.getArgumentName(i) + ")"); - } else { - writer.print(binding.getArgumentName(i)); - } - - if (type.isCompoundTypeWrapper()) { - writer.print(" == null) ? null : "); - writer.print(binding.getArgumentName(i)); - writer.print(".getBuffer())"); - numBufferOffsetArgs++; - //writer.print(", " + byteOffsetConversionArgName(numBufferOffsetArgs)); - writer.print(", BufferFactory.getDirectBufferByteOffset(((" + binding.getArgumentName(i)); - writer.print(" == null) ? null : " + binding.getArgumentName(i) + ".getBuffer()))"); - } - needComma = true; - ++numArgsEmitted; - if(type.isNIOBuffer() || type.isNIOBufferArray()) { - if(!type.isArray()) { - numBufferOffsetArgs++; - if(generateDirectAndIndirect) { - if(!indirectCase) { - writer.print - (", BufferFactory.getDirectBufferByteOffset(" + binding.getArgumentName(i) + ")"); - } else { - writer.print - (", BufferFactory.getIndirectBufferByteOffset(" + binding.getArgumentName(i) + ")"); - } - } else { - writer.print - (", BufferFactory.getDirectBufferByteOffset(" + binding.getArgumentName(i) + ")"); - } - } else { - numBufferOffsetArrayArgs++; - writer.print(", " + byteOffsetArrayConversionArgName(numBufferOffsetArrayArgs)); - } - } - - // Add Array offset parameter for primitive arrays - if(type.isArray() && !type.isNIOBufferArray() && !type.isStringArray()) { - // writer.print(", " + binding.getArgumentName(i) + "_offset"); - if(type.isFloatArray()) { - writer.print(", BufferFactory.SIZEOF_FLOAT * " + binding.getArgumentName(i) + "_offset"); - } else if(type.isDoubleArray()) { - writer.print(", BufferFactory.SIZEOF_DOUBLE * " + binding.getArgumentName(i) + "_offset"); - } else if(type.isByteArray()) { - writer.print(", " + binding.getArgumentName(i) + "_offset"); - } else if(type.isLongArray()) { - writer.print(", BufferFactory.SIZEOF_LONG * " + binding.getArgumentName(i) + "_offset"); - } else if(type.isShortArray()) { - writer.print(", BufferFactory.SIZEOF_SHORT * " + binding.getArgumentName(i) + "_offset"); - } else if(type.isIntArray()) { - writer.print(", BufferFactory.SIZEOF_INT * " + binding.getArgumentName(i) + "_offset"); - } else { - throw new RuntimeException("Unsupported type for calculating array offset argument for " + - binding.getArgumentName(i) + "-- error occurred while processing Java glue code for " + binding.getName()); - } - } - - } - return numArgsEmitted; - } - - protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { - JavaType returnType = binding.getJavaReturnType(); - boolean indirect; - - indirect = isGenerateIndirectBufferInterface(); - - if (returnType.isCompoundTypeWrapper()) { - if(!indirect) writer.println(";"); - String fmt = getReturnedArrayLengthExpression(); - writer.println(" if (_res == null) return null;"); - if (fmt == null) { - writer.print(" return new " + returnType.getName() + "(_res.order(ByteOrder.nativeOrder()))"); - } else { - writer.println(" _res.order(ByteOrder.nativeOrder());"); - String[] argumentNames = new String[binding.getNumArguments()]; - for (int i = 0; i < binding.getNumArguments(); i++) { - argumentNames[i] = binding.getArgumentName(i); - } - String expr = new MessageFormat(fmt).format(argumentNames); - PointerType cReturnTypePointer = binding.getCReturnType().asPointer(); - CompoundType cReturnType = null; - if (cReturnTypePointer != null) { - cReturnType = cReturnTypePointer.getTargetType().asCompound(); - } - if (cReturnType == null) { - throw new RuntimeException("ReturnedArrayLength directive currently only supported for pointers to compound types " + - "(error occurred while generating Java glue code for " + binding.getName() + ")"); - } - writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[" + expr + "];"); - writer.println(" for (int _count = 0; _count < " + expr + "; _count++) {"); - // Create temporary ByteBuffer slice - // FIXME: probably need Type.getAlignedSize() for arrays of - // compound types (rounding up to machine-dependent alignment) - writer.println(" _res.position(_count * " + cReturnType.getSize() + ");"); - writer.println(" _res.limit ((1 + _count) * " + cReturnType.getSize() + ");"); - writer.println(" ByteBuffer _tmp = _res.slice();"); - writer.println(" _tmp.order(ByteOrder.nativeOrder());"); - writer.println(" _res.position(0);"); - writer.println(" _res.limit(_res.capacity());"); - writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_tmp);"); - writer.println(" }"); - writer.print (" return _retarray"); - } - writer.println(";"); - } else if (returnType.isNIOBuffer()) { - if(!indirect) writer.println(";"); - writer.println(" if (_res == null) return null;"); - writer.print(" return _res.order(ByteOrder.nativeOrder())"); - writer.println(";"); - } else if (returnType.isArrayOfCompoundTypeWrappers()) { - if(!indirect) writer.println(";"); - writer.println(" if (_res == null) return null;"); - writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); - writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); - writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_res[_count]);"); - writer.println(" }"); - writer.print (" return _retarray"); - writer.println(";"); - } else { - if(!indirect) writer.println(";"); - } - } -} - diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java index 8917bede1..18c9c301d 100644 --- a/src/net/java/games/gluegen/JavaType.java +++ b/src/net/java/games/gluegen/JavaType.java @@ -222,8 +222,7 @@ public class JavaType { } /** - * Returns the name corresponding to this type. Returns null when this - * object does not represent a C-language "struct" type. + * Returns the Java type name corresponding to this type. */ public String getName() { if (clazz != null) { @@ -242,6 +241,16 @@ public class JavaType { or NULL if it can't be represented (i.e., it's a boxing class that we need to call getBuffer() on.) */ public String jniTypeName() { + if (isCompoundTypeWrapper()) { + // These are sent down as Buffers (e.g., jobject) + return "jobject"; + } + + if (isArrayOfCompoundTypeWrappers()) { + // These are returned as arrays of ByteBuffers (e.g., jobjectArray) + return "jobjectArray /* of ByteBuffer */"; + } + if (clazz == null) { return null; } @@ -250,48 +259,41 @@ public class JavaType { return "void"; } - if (clazz.isPrimitive()) { + if (isPrimitive()) { return "j" + clazz.getName(); } - if (clazz.isArray()) { - Class elementType = clazz.getComponentType(); - if (elementType.isPrimitive()) - { - // Type is array-of-primitive - return "j" + elementType.getName() + "Array"; - } - else if (elementType == java.lang.String.class) - { - // Type is array-of-string + if (isPrimitiveArray() || isNIOBuffer()) { + // We now pass primitive arrays and buffers uniformly down to native code as java.lang.Object. + return "jobject"; + } + + if (isArray()) { + if (isStringArray()) { return "jobjectArray /*elements are String*/"; - //return "jobjectArray"; } - else if (java.nio.Buffer.class.isAssignableFrom(elementType)) - { + + Class elementType = clazz.getComponentType(); + + if (isNIOBufferArray()) { return "jobjectArray /*elements are " + elementType.getName() + "*/"; } - else if (elementType.isArray()) - { + + if (elementType.isArray()) { // Type is array-of-arrays-of-something - if (elementType.getComponentType().isPrimitive()) - { + if (elementType.getComponentType().isPrimitive()) { // Type is an array-of-arrays-of-primitive return "jobjectArray /* elements are " + elementType.getComponentType() + "[]*/"; //return "jobjectArray"; - } - else - { + } else { throw new RuntimeException("Multi-dimensional arrays of types that are not primitives or Strings are not supported."); } } - else - { - // Some unusual type that we don't handle - throw new RuntimeException("Unexpected and unsupported type: \"" + this + "\""); - } - } // end array type case + + // Some unusual type that we don't handle + throw new RuntimeException("Unexpected and unsupported array type: \"" + this + "\""); + } if (isString()) { return "jstring"; @@ -452,6 +454,11 @@ public class JavaType { // Internals only below this point // + // For debugging + public void dump() { + System.err.println("[clazz = " + clazz + " , name = " + name + " , elementType = " + elementType + " , primitivePointerType = " + primitivePointerType + "]"); + } + /** * Constructs a representation for a type corresponding to the given Class * argument. diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java index 11513ed36..7576ec3f8 100644 --- a/src/net/java/games/gluegen/MethodBinding.java +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -53,9 +53,14 @@ public class MethodBinding { private JavaType javaReturnType; private List javaArgumentTypes; private boolean computedSignatureProperties; + private boolean argumentsUseNIO; private boolean signatureUsesNIO; + private boolean signatureCanUseIndirectNIO; + private boolean signatureUsesCompoundTypeWrappers; + private boolean signatureUsesCVoidPointers; + private boolean signatureUsesCPrimitivePointers; private boolean signatureUsesCArrays; - private boolean signatureUsesPrimitiveArrays; + private boolean signatureUsesJavaPrimitiveArrays; private JavaType containingType; private Type containingCType; private int thisPointerIndex = -1; @@ -68,15 +73,20 @@ public class MethodBinding { public MethodBinding(MethodBinding bindingToCopy) { this.sym = bindingToCopy.sym; - this.containingType = bindingToCopy.containingType; - this.containingCType = bindingToCopy.containingCType; - this.javaReturnType = bindingToCopy.javaReturnType; - this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); - this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; - this.signatureUsesNIO = bindingToCopy.signatureUsesNIO; - this.signatureUsesCArrays = bindingToCopy.signatureUsesCArrays; - this.signatureUsesPrimitiveArrays = bindingToCopy.signatureUsesPrimitiveArrays; - this.thisPointerIndex = bindingToCopy.thisPointerIndex; + this.containingType = bindingToCopy.containingType; + this.containingCType = bindingToCopy.containingCType; + this.javaReturnType = bindingToCopy.javaReturnType; + this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); + this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; + this.argumentsUseNIO = bindingToCopy.argumentsUseNIO; + this.signatureUsesNIO = bindingToCopy.signatureUsesNIO; + this.signatureCanUseIndirectNIO = bindingToCopy.signatureCanUseIndirectNIO; + this.signatureUsesCompoundTypeWrappers = bindingToCopy.signatureUsesCompoundTypeWrappers; + this.signatureUsesCVoidPointers = bindingToCopy.signatureUsesCVoidPointers; + this.signatureUsesCPrimitivePointers = bindingToCopy.signatureUsesCPrimitivePointers; + this.signatureUsesCArrays = bindingToCopy.signatureUsesCArrays; + this.signatureUsesJavaPrimitiveArrays = bindingToCopy.signatureUsesJavaPrimitiveArrays; + this.thisPointerIndex = bindingToCopy.thisPointerIndex; } /** Constructor for calling a C function. */ @@ -146,11 +156,11 @@ public class MethodBinding { return sym.getName(); } - /** Replaces the C primitive pointer argument at slot <i>argumentNumber</i> - (0..getNumArguments() - 1) with the specified type. If - argumentNumber is less than 0 then replaces the return type. */ - public MethodBinding createCPrimitivePointerVariant(int argumentNumber, - JavaType newArgType) { + /** Creates a new MethodBinding replacing the specified Java + argument type with a new argument type. If argumentNumber is + less than 0 then replaces the return type. */ + public MethodBinding replaceJavaArgumentType(int argumentNumber, + JavaType newArgType) { MethodBinding binding = new MethodBinding(sym); if (argumentNumber < 0) { binding.setJavaReturnType(newArgType); @@ -168,6 +178,16 @@ public class MethodBinding { } /** + * Returns true if any of the outgoing arguments in the method's + * signature require conversion or checking due to the use of New + * I/O. + */ + public boolean argumentsUseNIO() { + computeSignatureProperties(); + return argumentsUseNIO; + } + + /** * Returns true if the return type or any of the outgoing arguments * in the method's signature require conversion or checking due to * the use of New I/O. @@ -178,9 +198,55 @@ public class MethodBinding { } /** - * Returns true if any of the outgoing arguments in the method's - * signature represent fixed-length C arrays which require length - * checking during the call. + * Returns true if it is possible for any of the outgoing arguments + * to be indirect NIO buffers. + */ + public boolean signatureCanUseIndirectNIO() { + computeSignatureProperties(); + return signatureCanUseIndirectNIO; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature use "compound type wrappers", or + * NIO-based wrappers for C data structures. + */ + public boolean signatureUsesCompoundTypeWrappers() { + computeSignatureProperties(); + return signatureUsesCompoundTypeWrappers; + } + + /** + * Returns true if the function needs NIO-related + * wrapping/unwrapping or conversion of various arguments. Currently + * this returns the logical OR of signatureUsesNIO() and + * signatureUsesCompoundTypeWrappers(). + */ + public boolean needsNIOWrappingOrUnwrapping() { + return (signatureUsesNIO() || signatureUsesCompoundTypeWrappers()); + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C void* pointers. + */ + public boolean signatureUsesCVoidPointers() { + computeSignatureProperties(); + return signatureUsesCVoidPointers; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C primitive pointers. + */ + public boolean signatureUsesCPrimitivePointers() { + computeSignatureProperties(); + return signatureUsesCPrimitivePointers; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C arrays. */ public boolean signatureUsesCArrays() { computeSignatureProperties(); @@ -188,13 +254,12 @@ public class MethodBinding { } /** - * Returns true if any of the outgoing arguments in the method's - * signature represent primitive arrays which require a - * GetPrimitiveArrayCritical or similar operation during the call. + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent Java primitive arrays. */ - public boolean signatureUsesPrimitiveArrays() { + public boolean signatureUsesJavaPrimitiveArrays() { computeSignatureProperties(); - return signatureUsesPrimitiveArrays; + return signatureUsesJavaPrimitiveArrays; } /** @@ -205,68 +270,91 @@ public class MethodBinding { if (computedSignatureProperties) return; + argumentsUseNIO = false; signatureUsesNIO = false; + signatureCanUseIndirectNIO = false; + signatureUsesCompoundTypeWrappers = false; + signatureUsesCVoidPointers = false; + signatureUsesCPrimitivePointers = false; signatureUsesCArrays = false; - signatureUsesPrimitiveArrays = false; + signatureUsesJavaPrimitiveArrays = false; - if (javaReturnType.isCompoundTypeWrapper() || - javaReturnType.isNIOByteBuffer() || + if (javaReturnType.isCompoundTypeWrapper()) { + // Needs wrapping and/or setting of byte order (neither of which + // can be done easily from native code) + signatureUsesCompoundTypeWrappers = true; + } + + if (javaReturnType.isNIOByteBuffer() || javaReturnType.isArrayOfCompoundTypeWrappers()) { - // Needs wrapping and/or setting of byte order (neither of - // which can be done easily from native code) + // Needs setting of byte order which can't be done easily from + // native code signatureUsesNIO = true; } + Type cRetType = sym.getReturnType(); + if (cRetType.isArray()) { + // Needs checking of array lengths + signatureUsesCArrays = true; + if (cRetType.asArray().getElementType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } + } + + if (cRetType.isPointer()) { + if (cRetType.asPointer().getTargetType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } else if (cRetType.asPointer().getTargetType().isVoid()) { + signatureUsesCVoidPointers = true; + } + } + for (int i = 0; i < getNumArguments(); i++) { JavaType javaArgType = getJavaArgumentType(i); Type cArgType = getCArgumentType(i); - if (javaArgType.isCompoundTypeWrapper() || - javaArgType.isNIOBuffer() || + if (javaArgType.isCompoundTypeWrapper()) { + // Needs unwrapping of accessors + signatureUsesCompoundTypeWrappers = true; + } + + if (javaArgType.isNIOBuffer() || javaArgType.isNIOBufferArray()) { - // Needs unwrapping of accessors or checking of direct - // buffer property + // Needs checking of direct buffer property signatureUsesNIO = true; + argumentsUseNIO = true; + + if (javaArgType.isNIOBuffer()) { + // Potential conversion to indirect buffer + signatureCanUseIndirectNIO = true; + } } if (cArgType.isArray()) { // Needs checking of array lengths signatureUsesCArrays = true; + if (cArgType.asArray().getElementType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } } - if (javaArgType.isPrimitiveArray() && !javaArgType.isStringArray() ) { + if (cArgType.isPointer()) { + if (cArgType.asPointer().getTargetType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } else if (cArgType.asPointer().getTargetType().isVoid()) { + signatureUsesCVoidPointers = true; + } + } + + if (javaArgType.isPrimitiveArray()) { // Needs getPrimitiveArrayCritical or similar construct // depending on native code calling convention - signatureUsesPrimitiveArrays = true; + signatureUsesJavaPrimitiveArrays = true; } } computedSignatureProperties = true; } - - public MethodBinding createNIOBufferVariant() { - if (!signatureUsesNIO()) { - return this; - } - MethodBinding binding = new MethodBinding(sym, containingType, containingCType); - binding.thisPointerIndex = thisPointerIndex; - if (javaReturnType.isCompoundTypeWrapper()) { - binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); - } else if (javaReturnType.isArrayOfCompoundTypeWrappers()) { - binding.setJavaReturnType(JavaType.forNIOByteBufferArrayClass()); - } else { - binding.setJavaReturnType(javaReturnType); - } - for (int i = 0; i < getNumArguments(); i++) { - JavaType type = getJavaArgumentType(i); - if (type.isCompoundTypeWrapper()) { - type = JavaType.forNIOBufferClass(); - } - binding.addJavaArgumentType(type); - } - return binding; - } - /** Indicates whether this MethodBinding is for a function pointer contained in a struct. */ public boolean hasContainingType() { diff --git a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java index e233d7155..ceee9f1be 100644 --- a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java @@ -44,37 +44,29 @@ import java.util.*; import net.java.games.gluegen.*; import net.java.games.gluegen.cgram.types.*; -public class CGLPAWrapperEmitter extends CMethodBindingEmitter -{ +public class CGLPAWrapperEmitter extends CMethodBindingEmitter { private static final CommentEmitter defaultCommentEmitter = new CGLPAWrapperCommentEmitter(); - private CMethodBindingEmitter emitterBeingWrapped; private String glFuncPtrTypedefValue; private static String procAddressJavaTypeName = JavaType.createForClass(Long.TYPE).jniTypeName(); - protected boolean arrayImplRoutine = false; - - public CGLPAWrapperEmitter(CMethodBindingEmitter methodToWrap) - { + public CGLPAWrapperEmitter(CMethodBindingEmitter methodToWrap) { super( new MethodBinding(methodToWrap.getBinding()) { public String getName() { return GLEmitter.WRAP_PREFIX + super.getName(); } }, - methodToWrap.getIsOverloadedBinding(), + methodToWrap.getDefaultOutput(), methodToWrap.getJavaPackageName(), methodToWrap.getJavaClassName(), + methodToWrap.getIsOverloadedBinding(), methodToWrap.getIsJavaMethodStatic(), - methodToWrap.getDefaultOutput() - ); - -// if(binding.signatureUsesPrimitiveArrays()) -// arrayImplRoutine = true; - - indirectBufferInterface = methodToWrap.isIndirectBufferInterface(); + true, + methodToWrap.forIndirectBufferAndArrayImplementation() + ); if (methodToWrap.getReturnValueCapacityExpression() != null) { setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); @@ -88,8 +80,7 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter setCommentEmitter(defaultCommentEmitter); } - protected int emitArguments(PrintWriter writer) - { + protected int emitArguments(PrintWriter writer) { int numEmitted = super.emitArguments(writer); if (numEmitted > 0) { @@ -103,8 +94,7 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter return numEmitted; } - protected void emitBodyVariableDeclarations(PrintWriter writer) - { + protected void emitBodyVariableDeclarations(PrintWriter writer) { // create variable for the function pointer with the right type, and set // it to the value of the passed-in glProcAddress FunctionSymbol cSym = getBinding().getCSymbol(); @@ -121,8 +111,7 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter } protected void emitBodyVariablePreCallSetup(PrintWriter writer, - boolean emittingPrimitiveArrayCritical) - { + boolean emittingPrimitiveArrayCritical) { super.emitBodyVariablePreCallSetup(writer, emittingPrimitiveArrayCritical); if (!emittingPrimitiveArrayCritical) { @@ -143,23 +132,17 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter } } - // FIXME: refactor this and the superclass version so we don't have to copy - // the whole function - protected void emitBodyCallCFunction(PrintWriter writer) - { + protected void emitBodyCallCFunction(PrintWriter writer) { // Make the call to the actual C function writer.print(" "); // WARNING: this code assumes that the return type has already been // typedef-resolved. - Type cReturnType = getBinding().getCReturnType(); + Type cReturnType = binding.getCReturnType(); if (!cReturnType.isVoid()) { writer.print("_res = "); } - - // !!!!!!!!! BEGIN CHANGES FROM SUPERCLASS METHOD - MethodBinding binding = getBinding(); if (binding.hasContainingType()) { // Cannot call GL func through function pointer @@ -171,106 +154,16 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter writer.print("(* ptr_"); writer.print(binding.getCSymbol().getName()); writer.print(") "); - - // !!!!!!!!! END CHANGES FROM SUPERCLASS METHOD - - writer.print("("); - for (int i = 0; i < binding.getNumArguments(); i++) { - if (i != 0) { - writer.print(", "); - } - JavaType javaType = binding.getJavaArgumentType(i); - // Handle case where only param is void. - if (javaType.isVoid()) { - // Make sure this is the only param to the method; if it isn't, - // there's something wrong with our parsing of the headers. - assert(binding.getNumArguments() == 1); - continue; - } - - if (javaType.isJNIEnv()) { - writer.print("env"); - } else if (binding.isArgumentThisPointer(i)) { - writer.print(CMethodBindingEmitter.cThisArgumentName()); - } else { - writer.print("("); - writer.print(binding.getCSymbol().getArgumentType(i).getName()); - writer.print(") "); - if (binding.getCArgumentType(i).isPointer() && binding.getJavaArgumentType(i).isPrimitive()) { - writer.print("(intptr_t) "); - } - if (javaType.isArray() || javaType.isNIOBuffer()) { - - Type cArgType = binding.getCSymbol().getArgumentType(i); - boolean containsVoid = false; - // Add special code for accounting for array offsets - // - // For mapping from byte primitive array type to type* case produces code: - // (GLtype*)((char*)_ptr0 + varName_offset) - // where varName_offset is the number of bytes offset as calculated in Java code - - if(javaType.isArray() && !javaType.isNIOBufferArray() && !javaType.isStringArray()) { - writer.print("( (char*)"); - } - /* End of special code section for accounting for array offsets */ - - writer.print(pointerConversionArgumentName(i)); - if (javaArgTypeNeedsDataCopy(javaType)) { - writer.print("_copy"); - } - - /* Add special code for accounting for array offsets */ - if(javaType.isArray() && !javaType.isNIOBufferArray() && !javaType.isStringArray()) { - writer.print(" + " + binding.getArgumentName(i) + "_offset)"); - } - /* End of special code section for accounting for array offsets */ - - } else { - if (javaType.isString()) { writer.print("_UTF8"); } - writer.print(binding.getArgumentName(i)); - } - } - } + emitBodyPassCArguments(writer); writer.println(");"); } protected String jniMangle(MethodBinding binding) { - StringBuffer buf = new StringBuffer(); - buf.append(jniMangle(binding.getName())); - - if( isIndirectBufferInterface() ) - buf.append("2"); - else if(binding.signatureUsesPrimitiveArrays()) - buf.append("1"); - - buf.append("__"); - for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); - Class c = type.getJavaClass(); - if (c != null) { - jniMangle(c, buf); - // If Buffer offset arguments were added, we need to mangle the JNI for the - // extra arguments - if(type.isNIOBuffer()) { - jniMangle(Integer.TYPE, buf); - } else if (type.isNIOBufferArray()) { - int[] intArrayType = new int[0]; - c = intArrayType.getClass(); - jniMangle(c , buf); - } - if(type.isArray() && !type.isNIOBufferArray() && !type.isStringArray()) { - jniMangle(Integer.TYPE, buf); - } - } else { - // FIXME: add support for char* -> String conversion - throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); - } - } - - jniMangle(Long.TYPE, buf); // to account for the additional _addr_ parameter + StringBuffer buf = new StringBuffer(super.jniMangle(binding)); + jniMangle(Long.TYPE, buf, false); // to account for the additional _addr_ parameter return buf.toString(); - } + } /** This class emits the comment for the wrapper method */ private static class CGLPAWrapperCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java index b3581bd73..e6c44bd4d 100644 --- a/src/net/java/games/gluegen/opengl/GLEmitter.java +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -145,16 +145,11 @@ public class GLEmitter extends JavaEmitter FunctionEmitter emitter = (FunctionEmitter) iter.next(); if (emitter instanceof JavaMethodBindingEmitter) { - JavaMethodBindingEmitter newEmitter = - generateModifiedEmitter((JavaMethodBindingEmitter)emitter); - if (newEmitter != null) { - modifiedEmitters.add(newEmitter); - } + generateModifiedEmitters((JavaMethodBindingEmitter) emitter, modifiedEmitters); } else if (emitter instanceof CMethodBindingEmitter) { - modifiedEmitters.add( - generateModifiedEmitter((CMethodBindingEmitter)emitter)); + generateModifiedEmitters((CMethodBindingEmitter) emitter, modifiedEmitters); } else { @@ -187,9 +182,40 @@ public class GLEmitter extends JavaEmitter // Internals only below this point // - protected JavaMethodBindingEmitter generateModifiedEmitter(JavaMethodBindingEmitter baseJavaEmitter) - { - if (!(baseJavaEmitter instanceof JavaMethodBindingImplEmitter)) { + protected void generateModifiedEmitters(JavaMethodBindingEmitter baseJavaEmitter, List emitters) { + if (getGLConfig().manuallyImplement(baseJavaEmitter.getName())) { + // User will provide Java-side implementation of this routine; + // pass through any emitters which will produce signatures for + // it unmodified + emitters.add(baseJavaEmitter); + return; + } + + JavaGLPAWrapperEmitter emitter = + new JavaGLPAWrapperEmitter(baseJavaEmitter, + getGLConfig().getProcAddressTableExpr(), + baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.PRIVATE)); + emitters.add(emitter); + + // If this emitter doesn't have a body (i.e., is a public native + // call), we need to force it to emit a body, and produce another + // one to act as the entry point + if (baseJavaEmitter.signatureOnly() && + baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.PUBLIC) && + baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.NATIVE)) { + emitter.setEmitBody(true); + emitter.removeModifier(JavaMethodBindingEmitter.NATIVE); + emitter = new JavaGLPAWrapperEmitter(baseJavaEmitter, + getGLConfig().getProcAddressTableExpr(), + true); + emitter.setForImplementingMethodCall(true); + emitters.add(emitter); + } + + /***** + FIXME: OLD CODE (DELETE) + + if (baseJavaEmitter.signatureOnly()) { // We only want to wrap the native entry point in the implementation // class, not the public interface in the interface class. // @@ -201,14 +227,11 @@ public class GLEmitter extends JavaEmitter return null; return baseJavaEmitter; } - if (getGLConfig().manuallyImplement(baseJavaEmitter.getName())) { - // User will provide Java-side implementation of this routine - return null; - } return new JavaGLPAWrapperEmitter(baseJavaEmitter, getGLConfig().getProcAddressTableExpr()); + ****/ } - protected CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) + protected void generateModifiedEmitters(CMethodBindingEmitter baseCEmitter, List emitters) { // The C-side JNI binding for this particular function will have an // extra final argument, which is the address (the OpenGL procedure @@ -218,7 +241,7 @@ public class GLEmitter extends JavaEmitter if (exp != null) { res.setReturnValueCapacityExpression(exp); } - return res; + emitters.add(res); } protected boolean needsProcAddressWrapper(FunctionSymbol sym) diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java index d0eafa8de..51f5579f4 100644 --- a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -44,18 +44,20 @@ import java.util.*; import net.java.games.gluegen.*; import net.java.games.gluegen.cgram.types.*; -public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter -{ - private static final CommentEmitter commentEmitterForWrappedMethod = +public class JavaGLPAWrapperEmitter extends JavaMethodBindingEmitter { + private final CommentEmitter commentEmitterForWrappedMethod = new WrappedMethodCommentEmitter(); - private JavaMethodBindingEmitter emitterBeingWrapped; + private boolean changeNameAndArguments; private String getProcAddressTableExpr; - public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap, String getProcAddressTableExpr) - { - super(methodToWrap.getBinding(), methodToWrap.getDefaultOutput(), methodToWrap.getRuntimeExceptionType()); + public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap, + String getProcAddressTableExpr, + boolean changeNameAndArguments) { + super(methodToWrap); + this.changeNameAndArguments = changeNameAndArguments; this.getProcAddressTableExpr = getProcAddressTableExpr; + setCommentEmitter(new WrappedMethodCommentEmitter()); if (methodToWrap.getBinding().hasContainingType()) { @@ -63,111 +65,39 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter "Cannot create OpenGL proc. address wrapper; method has containing type: \"" + methodToWrap.getBinding() + "\""); } + } - // make a new emitter that will emit the original method's binding, but - // with WRAP_PREFIX before its name. If a body is needed (for array - // length checking, unwrapping of wrapper objects to java.nio.Buffers, - // etc.) then it will be generated; therefore the emitter being wrapped - // should be an "NIO buffer variant" (i.e., after all unpacking has - // occurred). - emitterBeingWrapped = - new JavaMethodBindingEmitter(methodToWrap.getBinding().createNIOBufferVariant(), - methodToWrap.getDefaultOutput(), - methodToWrap.getRuntimeExceptionType()) - { - - protected void emitName(PrintWriter writer) - { - writer.print(GLEmitter.WRAP_PREFIX); - super.emitName(writer); - - if(getBinding().signatureUsesPrimitiveArrays()) - writer.print("1"); - - } - protected int emitArguments(PrintWriter writer) - { - // following is set to true so that Buffer offset parameters are generated - // in parent class method, when appropriate - prefixedMethod = true; - int numEmitted = super.emitArguments(writer); - if (numEmitted > 0) - { - writer.print(", "); - } - - writer.print("long glProcAddress"); - ++numEmitted; - - return numEmitted; - } - }; - - // copy the modifiers from the original emitter - emitterBeingWrapped.addModifiers(methodToWrap.getModifiers()); - - // Change the access of the method we're wrapping to PRIVATE - EmissionModifier origAccess = null; // null is equivalent if package access - if (emitterBeingWrapped.hasModifier(PUBLIC)) - { - origAccess = PUBLIC; - } - else if (emitterBeingWrapped.hasModifier(PROTECTED)) - { - origAccess = PROTECTED; - } - else if (emitterBeingWrapped.hasModifier(PRIVATE)) - { - origAccess = PRIVATE; - } - - if (origAccess != null) - { - emitterBeingWrapped.removeModifier(origAccess); - } - emitterBeingWrapped.addModifier(PRIVATE); - emitterBeingWrapped.addModifier(NATIVE); - - // Now make our binding use the original access of the wrapped method - this.addModifier(origAccess); - if (emitterBeingWrapped.hasModifier(STATIC)) { - this.addModifier(STATIC); + public String getName() { + String res = super.getName(); + if (changeNameAndArguments) { + return GLEmitter.WRAP_PREFIX + res; } + return res; } - protected boolean needsBody() { - return true; - } + protected int emitArguments(PrintWriter writer) { + int numEmitted = super.emitArguments(writer); + if (changeNameAndArguments) { + if (numEmitted > 0) { + writer.print(", "); + } - protected String getImplMethodName() { - if(getBinding().signatureUsesPrimitiveArrays()) - return GLEmitter.WRAP_PREFIX + getBinding().getName() + "1"; - else - return GLEmitter.WRAP_PREFIX + getBinding().getName(); + writer.print("long glProcAddress"); + ++numEmitted; + } + + return numEmitted; } - public void emit(PrintWriter writer) - { - // Emit a wrapper that will call the method we want to wrap - //writer.println(" // Emitter being wrapped = " + emitterBeingWrapped.getClass().getName()); - super.emit(writer); - writer.println(); - - // emit the wrapped method - CommentEmitter origComment = emitterBeingWrapped.getCommentEmitter(); - emitterBeingWrapped.setCommentEmitter(commentEmitterForWrappedMethod); - emitterBeingWrapped.emit(writer); - emitterBeingWrapped.setCommentEmitter(origComment); - writer.println(); + protected String getImplMethodName(boolean direct) { + return GLEmitter.WRAP_PREFIX + super.getImplMethodName(direct); } protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { super.emitPreCallSetup(binding, writer); - MethodBinding wrappedBinding = emitterBeingWrapped.getBinding(); String procAddressVariable = - GLEmitter.PROCADDRESS_VAR_PREFIX + wrappedBinding.getName(); - + GLEmitter.PROCADDRESS_VAR_PREFIX + binding.getName(); writer.println(" final long __addr_ = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (__addr_ == 0) {"); writer.println(" throw new GLException(\"Method \\\"" + binding.getName() + "\\\" not available\");"); @@ -184,7 +114,7 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter } /** This class emits the comment for the wrapper method */ - private static class WrappedMethodCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { + private class WrappedMethodCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { writer.print("Encapsulates function pointer for OpenGL function <br>: "); } diff --git a/src/net/java/games/gluegen/runtime/BufferFactory.java b/src/net/java/games/gluegen/runtime/BufferFactory.java index cae81b1b3..81334b77a 100644 --- a/src/net/java/games/gluegen/runtime/BufferFactory.java +++ b/src/net/java/games/gluegen/runtime/BufferFactory.java @@ -87,25 +87,25 @@ public class BufferFactory { the total offset for Direct Buffers. */ public static int getDirectBufferByteOffset(Buffer buf) { - if(buf == null) { - return 0; - } - if(buf instanceof ByteBuffer) { - return (buf.position()); - } else if (buf instanceof FloatBuffer) { - return (buf.position() * BufferUtils.SIZEOF_FLOAT); - } else if (buf instanceof IntBuffer) { - return (buf.position() * BufferUtils.SIZEOF_INT); - } else if (buf instanceof ShortBuffer) { - return (buf.position() * BufferUtils.SIZEOF_SHORT); - } else if (buf instanceof DoubleBuffer) { - return (buf.position() * BufferUtils.SIZEOF_DOUBLE); - } else if (buf instanceof LongBuffer) { - return (buf.position() * BufferUtils.SIZEOF_LONG); - } - - throw new RuntimeException("Disallowed array backing store type in buffer " - + buf.getClass().getName()); + if(buf == null) { + return 0; + } + if(buf instanceof ByteBuffer) { + return (buf.position()); + } else if (buf instanceof FloatBuffer) { + return (buf.position() * BufferUtils.SIZEOF_FLOAT); + } else if (buf instanceof IntBuffer) { + return (buf.position() * BufferUtils.SIZEOF_INT); + } else if (buf instanceof ShortBuffer) { + return (buf.position() * BufferUtils.SIZEOF_SHORT); + } else if (buf instanceof DoubleBuffer) { + return (buf.position() * BufferUtils.SIZEOF_DOUBLE); + } else if (buf instanceof LongBuffer) { + return (buf.position() * BufferUtils.SIZEOF_LONG); + } + + throw new RuntimeException("Disallowed array backing store type in buffer " + + buf.getClass().getName()); } @@ -113,25 +113,25 @@ public class BufferFactory { a Buffer object. */ public static Object getArray(Buffer buf) { - if (buf == null) { - return null; - } - if(buf instanceof ByteBuffer) { - return ((ByteBuffer) buf).array(); - } else if (buf instanceof FloatBuffer) { - return ((FloatBuffer) buf).array(); - } else if (buf instanceof IntBuffer) { - return ((IntBuffer) buf).array(); - } else if (buf instanceof ShortBuffer) { - return ((ShortBuffer) buf).array(); - } else if (buf instanceof DoubleBuffer) { - return ((DoubleBuffer) buf).array(); - } else if (buf instanceof LongBuffer) { - return ((LongBuffer) buf).array(); - } - - throw new RuntimeException("Disallowed array backing store type in buffer " - + buf.getClass().getName()); + if (buf == null) { + return null; + } + if(buf instanceof ByteBuffer) { + return ((ByteBuffer) buf).array(); + } else if (buf instanceof FloatBuffer) { + return ((FloatBuffer) buf).array(); + } else if (buf instanceof IntBuffer) { + return ((IntBuffer) buf).array(); + } else if (buf instanceof ShortBuffer) { + return ((ShortBuffer) buf).array(); + } else if (buf instanceof DoubleBuffer) { + return ((DoubleBuffer) buf).array(); + } else if (buf instanceof LongBuffer) { + return ((LongBuffer) buf).array(); + } + + throw new RuntimeException("Disallowed array backing store type in buffer " + + buf.getClass().getName()); } @@ -161,5 +161,4 @@ public class BufferFactory { throw new RuntimeException("Unknown buffer type " + buf.getClass().getName()); } - } diff --git a/src/net/java/games/jogl/Animator.java b/src/net/java/games/jogl/Animator.java index 15d31c224..ac6248487 100644 --- a/src/net/java/games/jogl/Animator.java +++ b/src/net/java/games/jogl/Animator.java @@ -67,6 +67,7 @@ public class Animator { private volatile boolean shouldStop; protected boolean ignoreExceptions; protected boolean printExceptions; + private boolean runAsFastAsPossible; // For efficient rendering of Swing components, in particular when // they overlap one another @@ -119,6 +120,14 @@ public class Animator { this.printExceptions = printExceptions; } + /** Sets a flag in this Animator indicating that it is to run as + fast as possible. By default there is a brief pause in the + animation loop which prevents the CPU from getting swamped. + This method may not have an effect on subclasses. */ + public final void setRunAsFastAsPossible(boolean runFast) { + runAsFastAsPossible = runFast; + } + /** Called every frame to cause redrawing of all of the GLAutoDrawables this Animator manages. Subclasses should call this to get the most optimized painting behavior for the set of @@ -174,10 +183,12 @@ public class Animator { } } display(); - // Avoid swamping the CPU - try { - Thread.sleep(1); - } catch (InterruptedException e) { + if (!runAsFastAsPossible) { + // Avoid swamping the CPU + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } } } } finally { diff --git a/src/net/java/games/jogl/GLJPanel.java b/src/net/java/games/jogl/GLJPanel.java index d3d95166c..c5b3b7d95 100644 --- a/src/net/java/games/jogl/GLJPanel.java +++ b/src/net/java/games/jogl/GLJPanel.java @@ -48,6 +48,7 @@ import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; +import java.nio.*; import java.security.*; import javax.swing.JComponent; import javax.swing.JPanel; @@ -98,8 +99,8 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { private BufferedImage offscreenImage; // One of these is used to store the read back pixels before storing // in the BufferedImage - private byte[] readBackBytes; - private int[] readBackInts; + private ByteBuffer readBackBytes; + private IntBuffer readBackInts; private int readBackWidthInPixels; private int readBackHeightInPixels; // Width of the actual GLJPanel @@ -514,7 +515,7 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { case BufferedImage.TYPE_3BYTE_BGR: glFormat = GL.GL_BGR; glType = GL.GL_UNSIGNED_BYTE; - readBackBytes = new byte[readBackWidthInPixels * readBackHeightInPixels * 3]; + readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3); break; case BufferedImage.TYPE_INT_RGB: @@ -523,7 +524,7 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { glType = (hardwareAccelerationDisabled ? offscreenContext.getOffscreenContextPixelDataType() : hwGLFormat); - readBackInts = new int[readBackWidthInPixels * readBackHeightInPixels]; + readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); break; default: @@ -554,12 +555,9 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { // Actually read the pixels. gl.glReadBuffer(GL.GL_FRONT); if (readBackBytes != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes, 0); + gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes); } else if (readBackInts != null) { - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel$Updater.display(): readBackInts.length == " + readBackInts.length); - } - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts, 0); + gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); } // Restore saved modes. @@ -581,12 +579,12 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { int destIncr = 0; if (readBackBytes != null) { - src = readBackBytes; + src = readBackBytes.array(); dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData(); srcIncr = readBackWidthInPixels * 3; destIncr = offscreenImage.getWidth() * 3; } else { - src = readBackInts; + src = readBackInts.array(); dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); srcIncr = readBackWidthInPixels; destIncr = offscreenImage.getWidth(); diff --git a/src/net/java/games/jogl/impl/Project.java b/src/net/java/games/jogl/impl/Project.java index 41cf9d45e..6138a4108 100755 --- a/src/net/java/games/jogl/impl/Project.java +++ b/src/net/java/games/jogl/impl/Project.java @@ -104,11 +104,11 @@ */ package net.java.games.jogl.impl; +import java.nio.*; + import net.java.games.jogl.*; import net.java.games.jogl.util.*; -import java.nio.DoubleBuffer; - /** * Project.java * <p/> @@ -589,6 +589,33 @@ class Project { double y, double deltaX, double deltaY, + IntBuffer viewport) { + if (deltaX <= 0 || deltaY <= 0) { + return; + } + + /* Translate and scale the picked region to the entire window */ + gl.glTranslated((viewport.get(2) - 2 * (x - viewport.get(0))) / deltaX, + (viewport.get(3) - 2 * (y - viewport.get(1))) / deltaY, + 0); + gl.glScaled(viewport.get(2) / deltaX, viewport.get(3) / deltaY, 1.0); + } + + /** + * Method gluPickMatrix + * + * @param x + * @param y + * @param deltaX + * @param deltaY + * @param viewport + * @param viewport_offset + */ + public void gluPickMatrix(GL gl, + double x, + double y, + double deltaX, + double deltaY, int[] viewport, int viewport_offset) { if (deltaX <= 0 || deltaY <= 0) { diff --git a/src/net/java/games/jogl/impl/mipmap/Mipmap.java b/src/net/java/games/jogl/impl/mipmap/Mipmap.java index 8b697f37c..c80d25a12 100644 --- a/src/net/java/games/jogl/impl/mipmap/Mipmap.java +++ b/src/net/java/games/jogl/impl/mipmap/Mipmap.java @@ -263,7 +263,7 @@ public class Mipmap { if( target == GL.GL_TEXTURE_2D || target == GL.GL_PROXY_TEXTURE_2D ) { proxyTarget = GL.GL_PROXY_TEXTURE_2D; gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne, - heightAtLevelOne, 0, format, type, (double[])null, 0 ); + heightAtLevelOne, 0, format, type, null ); } else if( (target == GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB) || (target == GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB) || (target == GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB) || @@ -272,12 +272,12 @@ public class Mipmap { (target == GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) ) { proxyTarget = GL.GL_PROXY_TEXTURE_CUBE_MAP_ARB; gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne, - heightAtLevelOne, 0, format, type, (double[])null, 0 ); + heightAtLevelOne, 0, format, type, null ); } else { assert( target == GL.GL_TEXTURE_1D || target == GL.GL_PROXY_TEXTURE_1D ); proxyTarget = GL.GL_PROXY_TEXTURE_1D; gl.glTexImage1D( proxyTarget, 1, internalFormat, widthAtLevelOne, - 0, format, type, (double[])null, 0 ); + 0, format, type, null ); } gl.glGetTexLevelParameteriv( proxyTarget, 1, GL.GL_TEXTURE_WIDTH, proxyWidth, 0 ); // does it fit? @@ -342,7 +342,7 @@ public class Mipmap { if( target == GL.GL_TEXTURE_3D || target == GL.GL_PROXY_TEXTURE_3D ) { proxyTarget = GL.GL_PROXY_TEXTURE_3D; gl.glTexImage3D( proxyTarget, 1, internalFormat, widthAtLevelOne, - heightAtLevelOne, depthAtLevelOne, 0, format, type, (double[])null, 0 ); + heightAtLevelOne, depthAtLevelOne, 0, format, type, null ); } gl.glGetTexLevelParameteriv( proxyTarget, 1, GL.GL_TEXTURE_WIDTH, proxyWidth, 0 ); // does it fit |