diff options
Diffstat (limited to 'src/net/java/games/gluegen/CMethodBindingEmitter.java')
-rw-r--r-- | src/net/java/games/gluegen/CMethodBindingEmitter.java | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java new file mode 100644 index 000000000..45035f640 --- /dev/null +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -0,0 +1,1141 @@ +/* + * 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 + * MIDROSYSTEMS, 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; + +import net.java.games.gluegen.cgram.types.*; + +/** Emits the C-side component of the Java<->C JNI binding. */ +public class CMethodBindingEmitter extends FunctionEmitter +{ + protected static final CommentEmitter defaultCommentEmitter = + new DefaultCommentEmitter(); + + private MethodBinding binding; + + /** Name of the package in which the corresponding Java method resides.*/ + private String packageName; + + /** Name of the class in which the corresponding Java method resides.*/ + private String className; + + /** + * Whether or not the Java<->C JNI binding for this emitter's MethodBinding + * is overloaded. + */ + private boolean isOverloadedBinding; + + /** + * Whether or not the Java-side of the Java<->C JNI binding for this + * emitter's MethodBinding is static. + */ + private boolean isJavaMethodStatic; + + /** + * Optional List of Strings containing temporary C variables to declare. + */ + private List/*<String>*/ temporaryCVariableDeclarations; + + /** + * Optional List of Strings containing assignments to temporary C variables + * to make after the call is completed. + */ + private List/*<String>*/ temporaryCVariableAssignments; + + /** + * Capacity of the return value in the event that it is encapsulated in a + * java.nio.Buffer. Is ignored if binding.getJavaReturnType().isNIOBuffer() + * == false; + */ + private MessageFormat returnValueCapacityExpression = null; + + // Note: the VC++ 6.0 compiler emits hundreds of warnings when the + // (necessary) null-checking code is enabled. This appears to just + // be a compiler bug, but would be good to track down exactly why it + // is happening. When the null checking is enabled for just the + // GetPrimitiveArrayCritical calls, there are five warnings + // generated for several thousand new if tests added to the code. + // Which ones are the ones at fault? The line numbers for the + // warnings are incorrect. + private static final boolean EMIT_NULL_CHECKS = true; + + /** + * Constructs an emitter for the specified binding, and sets a default + * comment emitter that will emit the signature of the C function that is + * being bound. + */ + public CMethodBindingEmitter(MethodBinding binding, + boolean isOverloadedBinding, + String javaPackageName, + String javaClassName, + boolean isJavaMethodStatic, + PrintWriter output) + { + super(output); + + assert(binding != null); + assert(javaClassName != null); + assert(javaPackageName != null); + + this.binding = binding; + this.packageName = javaPackageName; + this.className = javaClassName; + this.isOverloadedBinding = isOverloadedBinding; + this.isJavaMethodStatic = isJavaMethodStatic; + setCommentEmitter(defaultCommentEmitter); + } + + public final MethodBinding getBinding() { return binding; } + + public String getName() { + return binding.getName(); + } + + /** + * Get the expression for the capacity of the returned java.nio.Buffer. + */ + public final MessageFormat getReturnValueCapacityExpression() + { + return returnValueCapacityExpression; + } + + /** + * If this function returns a void* encapsulated in a + * java.nio.Buffer, 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 + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the capacity of the java.nio.Buffer + * returned from this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false + * </code> + */ + public final void setReturnValueCapacityExpression(MessageFormat expression) + { + returnValueCapacityExpression = expression; + + if (!binding.getJavaReturnType().isNIOBuffer()) + { + throw new IllegalArgumentException( + "Cannot specify return value capacity for a method that does not " + + "return java.nio.Buffer: \"" + binding + "\""); + } + } + + /** + * Sets up a List of Strings containing declarations for temporary C + * variables to be assigned to after the underlying function call. A + * null argument indicates that no manual declarations are to be made. + */ + public final void setTemporaryCVariableDeclarations(List/*<String>*/ arg) { + temporaryCVariableDeclarations = arg; + } + + /** + * Sets up a List of Strings containing assignments for temporary C + * variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be made. + */ + public final void setTemporaryCVariableAssignments(List/*<String>*/ arg) { + temporaryCVariableAssignments = arg; + } + + /** + * Get the name of the class in which the corresponding Java method + * resides. + */ + public String getJavaPackageName() { return packageName; } + + /** + * Get the name of the package in which the corresponding Java method + * resides. + */ + public String getJavaClassName() { return className; } + + /** + * Is the Java<->C JNI binding for this emitter's MethodBinding one of + * several overloaded methods with the same name? + */ + public final boolean getIsOverloadedBinding() { return isOverloadedBinding; } + + /** + * Is the Java side of the Java<->C JNI binding for this emitter's + * MethodBinding a static method?. + */ + public final boolean getIsJavaMethodStatic() { return isJavaMethodStatic; } + + protected void emitReturnType(PrintWriter writer) + { + writer.print("JNIEXPORT "); + writer.print(binding.getJavaReturnType().jniTypeName()); + writer.print(" JNICALL"); + } + + protected void emitName(PrintWriter writer) + { + writer.println(); // start name on new line + writer.print("Java_"); + writer.print(jniMangle(getJavaPackageName())); + writer.print("_"); + writer.print(jniMangle(getJavaClassName())); + writer.print("_"); + if (isOverloadedBinding) + { + writer.print(jniMangle(binding)); + //System.err.println("OVERLOADED MANGLING FOR " + binding.getName() + + // " = " + jniMangle(binding)); + } + else + { + writer.print(jniMangle(binding.getName())); + //System.err.println(" NORMAL MANGLING FOR " + binding.getName() + + // " = " + jniMangle(binding.getName())); + } + } + + protected int emitArguments(PrintWriter writer) + { + writer.print("JNIEnv *env, "); + int numEmitted = 1; // initially just the JNIEnv + if (isJavaMethodStatic && !binding.hasContainingType()) + { + writer.print("jclass"); + } + else + { + writer.print("jobject"); + } + writer.print(" _unused"); + ++numEmitted; + + if (binding.hasContainingType()) + { + // "this" argument always comes down in argument 0 as direct buffer + writer.print(", jobject " + JavaMethodBindingEmitter.javaThisArgumentName()); + } + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + // Handle case where only param is void + if (javaArgType.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 (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + writer.print(", "); + writer.print(javaArgType.jniTypeName()); + writer.print(" "); + writer.print(binding.getArgumentName(i)); + ++numEmitted; + } + + return numEmitted; + } + + + protected void emitBody(PrintWriter writer) + { + writer.println(" {"); + emitBodyVariableDeclarations(writer); + emitBodyUserVariableDeclarations(writer); + emitBodyVariablePreCallSetup(writer); + emitBodyCallCFunction(writer); + emitBodyUserVariableAssignments(writer); + emitBodyVariablePostCallCleanup(writer); + emitBodyReturnResult(writer); + writer.println("}"); + writer.println(); + } + + protected void emitBodyVariableDeclarations(PrintWriter writer) + { + // Emit declarations for all pointer and String conversion variables + if (binding.hasContainingType()) { + emitPointerDeclaration(writer, + binding.getContainingType(), + binding.getContainingCType(), + CMethodBindingEmitter.cThisArgumentName()); + } + + boolean emittedDataCopyTemps = false; + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + + if (type.isArray() || type.isNIOBuffer()) { + String convName = pointerConversionArgumentName(i); + // handle array/buffer argument types + boolean needsDataCopy = + emitPointerDeclaration(writer, + binding.getJavaArgumentType(i), + binding.getCArgumentType(i), + convName); + if (needsDataCopy && !emittedDataCopyTemps) { + // emit loop counter and array length variables used during data + // copy + writer.println(" jobject _tmpObj;"); + writer.println(" int _copyIndex;"); + writer.println(" jsize _arrayLen"+convName+";"); + emittedDataCopyTemps = true; + } + } else if (type.isString()) { + writer.print(" const char* _UTF8"); + writer.print(binding.getArgumentName(i)); + writer.println(" = NULL;"); + } + + } + + // Emit declaration for return value if necessary + Type cReturnType = binding.getCReturnType(); + + JavaType javaReturnType = binding.getJavaReturnType(); + String arrayResLength = "_array_res_length"; + String arrayRes = "_array_res"; + String capitalizedComponentType = null; + if (!cReturnType.isVoid()) { + writer.print(" "); + // Note we must respect const/volatile for return argument + writer.print(binding.getCSymbol().getReturnType().getName(true)); + writer.println(" _res;"); + if (javaReturnType.isArray()) { + 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"); + } + + 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(";"); + } + } + } + + /** Emits the user-defined C variable declarations from the + TemporaryCVariableDeclarations directive in the .cfg file. */ + protected void emitBodyUserVariableDeclarations(PrintWriter writer) { + if (temporaryCVariableDeclarations != null) { + for (Iterator iter = temporaryCVariableDeclarations.iterator(); iter.hasNext(); ) { + String val = (String) iter.next(); + writer.print(" "); + writer.println(val); + } + } + } + + /** + * Code to init the variables that were declared in + * emitBodyVariableDeclarations(), PRIOR TO calling the actual C + * function. + */ + protected void emitBodyVariablePreCallSetup(PrintWriter writer) + { + // Convert all Buffers to pointers first so we don't have to + // call ReleasePrimitiveArrayCritical for any arrays if any + // incoming buffers aren't direct + if (binding.hasContainingType()) { + emitPointerConversion(writer, binding, + binding.getContainingType(), + binding.getContainingCType(), + JavaMethodBindingEmitter.javaThisArgumentName(), + CMethodBindingEmitter.cThisArgumentName()); + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + if (type.isNIOBuffer()) { + emitPointerConversion(writer, binding, type, + binding.getCArgumentType(i), + binding.getArgumentName(i), + pointerConversionArgumentName(i)); + } + } + + // Convert all arrays to pointers, and get UTF-8 versions of jstring args + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + + if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + + if (javaArgType.isArray()) { + + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + writer.print(" "); + String convName = pointerConversionArgumentName(i); + writer.print(convName); + writer.print(" = ("); + Type cArgType = binding.getCArgumentType(i); + String cArgTypeName = cArgType.getName(); + if (javaArgType.isArray() && + javaArgType.getJavaClass().getComponentType() == java.lang.String.class) { + // java-side type is String[] + cArgTypeName = "jstring *"; + } + writer.print(cArgTypeName); + writer.print(") (*env)->GetPrimitiveArrayCritical(env, "); + writer.print(binding.getArgumentName(i)); + writer.println(", NULL);"); + + // Handle the case where the array elements are of a type that needs a + // data copy operation to convert from the java memory model to the C + // memory model (e.g., int[][], String[], etc) + // + // FIXME: should factor out this whole block of code into a separate + // method for clarity and maintenance purposes + Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); + boolean needsDataCopy = + subArrayElementJavaType.isArray() || + subArrayElementJavaType == java.lang.String.class; + if (needsDataCopy) + { + if (cArgType.toString().indexOf("const") == -1) { + // FIXME: if the arg type is non-const, the sematics might be that + // the function modifies the argument -- we don't yet support + // this. + // + // Note: the check for "const" in the CVAttributes string isn't + // truly checking the constness of the target types at both + // pointer depths. However, it's a quick approximation, and quite + // often C code doesn't get the constness right anyhow. + throw new RuntimeException( + "Cannot copy data for ptr-to-ptr arg type \"" + cArgType + + "\": support for non-const ptr-to-ptr types not implemented."); + } + + writer.println(); + writer.println(" /* Copy contents of " + convName + + " into " + convName + "_copy */"); + + // get length of array being copied + String arrayLenName = "_arrayLen"+convName; + writer.print(" "); + writer.print(arrayLenName); + writer.print(" = (*env)->GetArrayLength(env, "); + writer.print(binding.getArgumentName(i)); + writer.println(");"); + + // allocate an array to hold each element + if (cArgType.pointerDepth() != 2) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; copying only supported for types of the form " + + "ptr-to-ptr-to-primitive."); + } + PointerType cArgPtrType = cArgType.asPointer(); + if (cArgPtrType == null) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; currently only pointer types supported."); + } + PointerType cArgElementType = cArgPtrType.getTargetType().asPointer(); + emitMalloc( + writer, + convName+"_copy", + cArgElementType.getName(), + arrayLenName, + "Could not allocate buffer for copying data in argument \\\""+binding.getArgumentName(i)+"\\\""); + + // 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(binding.getArgumentName(i)); + writer.println(", _copyIndex);"); + + if (subArrayElementJNITypeString == "jstring") + { + writer.print(" "); + emitGetStringUTFChars(writer, + "(jstring) _tmpObj", + "(const char*)"+convName+"_copy[_copyIndex]"); + } + 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? + // Probably depends on const-ness of the argument. + // 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)+"\\\""); + // FIXME: copy the data (use Get<type>ArrayRegion() calls) + if (true) throw new RuntimeException( + "Cannot yet handle type \"" + cArgType.getName() + + "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays"); + + + } + writer.println(" }"); + + writer.println(); + } // end of data copy + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + + } else if (javaArgType.isString()) { + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + emitGetStringUTFChars(writer, + binding.getArgumentName(i), + "_UTF8" + binding.getArgumentName(i)); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + } + } + + + /** + * Code to clean up any variables that were declared in + * emitBodyVariableDeclarations(), AFTER calling the actual C function. + */ + protected void emitBodyVariablePostCallCleanup(PrintWriter writer) + { + // Release primitive arrays and temporary UTF8 strings if necessary + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + if (javaArgType.isArray()) { + String convName = pointerConversionArgumentName(i); + + // Release array + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + // clean up the case where the array elements are of a type that needed + // a data copy operation to convert from the java memory model to the + // C memory model (e.g., int[][], String[], etc) + // + // FIXME: should factor out this whole block of code into a separate + // method for clarity and maintenance purposes + Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); + boolean needsDataCopy = + subArrayElementJavaType.isArray() || + subArrayElementJavaType == java.lang.String.class; + if (needsDataCopy) + { + Type cArgType = binding.getCArgumentType(i); + String cArgTypeName = cArgType.getName(); + + if (cArgType.toString().indexOf("const") == -1) { + // FIXME: handle any cleanup from treatment of non-const args, + // assuming they were treated differently in + // emitBodyVariablePreCallSetup() (see the similar section in that + // method for details). + throw new RuntimeException( + "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType + + "\": support for cleaning up non-const ptr-to-ptr types not implemented."); + } + + writer.println(" /* Clean up " + convName + "_copy */"); + String arrayLenName = "_arrayLen" + convName; + + // free each element + PointerType cArgPtrType = cArgType.asPointer(); + if (cArgPtrType == null) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; currently only pointer types supported."); + } + PointerType cArgElementType = cArgPtrType.getTargetType().asPointer(); + + // process each element in the array + writer.println(" for (_copyIndex = 0; _copyIndex < " + arrayLenName +"; ++_copyIndex) {"); + + // 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(binding.getArgumentName(i)); + writer.println(", _copyIndex);"); + + if (subArrayElementJNITypeString == "jstring") + { + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print("(jstring) _tmpObj"); + writer.print(", "); + writer.print(convName+"_copy[_copyIndex]"); + writer.println(");"); + } + else + { + // FIXME: free up stuff here + if (true) throw new RuntimeException( + "Cannot yet handle type \"" + cArgType.getName() + + "\"; need to add support for cleaning up copied ptr-to-ptr-to-primitiveType subarrays"); + } + + writer.println(" }"); + // free the main array + writer.print(" free("); + writer.print(convName+"_copy"); + writer.println(");"); + + + writer.println(); + } // end of cleaning up copied data + + writer.print(" (*env)->ReleasePrimitiveArrayCritical(env, "); + writer.print(binding.getArgumentName(i)); + writer.print(", "); + writer.print(convName); + writer.println(", JNI_ABORT);"); + + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } else if (javaArgType.isString()) { + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print(binding.getArgumentName(i)); + writer.print(", _UTF8"); + writer.print(binding.getArgumentName(i)); + writer.println(");"); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + } + } + + 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("("); + for (int i = 0; i < binding.getNumArguments(); i++) { + if (i != 0) { + writer.print(", "); + } + JavaType javaArgType = binding.getJavaArgumentType(i); + // Handle case where only param is void. + if (javaArgType.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 (javaArgType.isJNIEnv()) { + writer.print("env"); + } else if (binding.isArgumentThisPointer(i)) { + writer.print(CMethodBindingEmitter.cThisArgumentName()); + } else { + writer.print("("); + Type cArgType = binding.getCSymbol().getArgumentType(i); + writer.print(cArgType.getName()); + writer.print(") "); + if (binding.getCArgumentType(i).isPointer() && binding.getJavaArgumentType(i).isPrimitive()) { + writer.print("(intptr_t) "); + } + if (javaArgType.isArray() || javaArgType.isNIOBuffer()) { + writer.print(pointerConversionArgumentName(i)); + } else { + if (javaArgType.isString()) { writer.print("_UTF8"); } + writer.print(binding.getArgumentName(i)); + } + } + } + writer.println(");"); + } + + /** Emits the user-defined C variable assignments from the + TemporaryCVariableAssignments directive in the .cfg file. */ + protected void emitBodyUserVariableAssignments(PrintWriter writer) { + if (temporaryCVariableAssignments != null) { + for (Iterator iter = temporaryCVariableAssignments.iterator(); iter.hasNext(); ) { + String val = (String) iter.next(); + writer.print(" "); + writer.println(val); + } + } + } + + // 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 + // typedef-resolved. + Type cReturnType = binding.getCReturnType(); + + // Return result if necessary + if (!cReturnType.isVoid()) { + JavaType javaReturnType = binding.getJavaReturnType(); + if (javaReturnType.isPrimitive()) { + writer.print(" return "); + if (cReturnType.isPointer()) { + // Pointer being converted to int or long: cast this result + // (through intptr_t to avoid compiler warnings with gcc) + writer.print("(" + javaReturnType.jniTypeName() + ") (intptr_t) "); + } + writer.println("_res;"); + } else if (javaReturnType.isNIOBuffer()) { + writer.println(" if (_res == NULL) return NULL;"); + writer.print(" return (*env)->NewDirectByteBuffer(env, _res, "); + // See whether capacity has been specified + if (returnValueCapacityExpression != null) { + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) + { + argumentNames[i] = binding.getArgumentName(i); + } + writer.print( + returnValueCapacityExpression.format(argumentNames)); + } else { + int sz = 0; + if (cReturnType.isPointer() && + cReturnType.asPointer().getTargetType().isCompound()) { + sz = cReturnType.asPointer().getTargetType().getSize(); + if (sz == -1) { + throw new InternalError( + "Error emitting code for compound return type "+ + "for function \"" + binding + "\": " + + "Structs to be emitted should have been laid out by this point " + + "(type " + cReturnType.asPointer().getTargetType().getName() + " / " + + cReturnType.asPointer().getTargetType() + " was not)" + ); + } + } else { + sz = cReturnType.getSize(); + } + writer.print(sz); + System.err.println( + "WARNING: No capacity specified for java.nio.Buffer return " + + "value for function \"" + binding + "\";" + + " assuming size of equivalent C return type (" + sz + " bytes): " + binding); + } + writer.println(");"); + } else if (javaReturnType.isString()) { + writer.print(" if (_res == NULL) return NULL;"); + writer.println(" return (*env)->NewStringUTF(env, _res);"); + } 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(";"); + + } + } + } + + protected static String cThisArgumentName() { + return "this0"; + } + + // Mangle a class, package or function name + protected String jniMangle(String name) { + return name.replaceAll("_", "_1").replace('.', '_'); + } + + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(jniMangle(binding.getName())); + 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); + } 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()) { + if (c == Boolean.TYPE) res.append("Z"); + else if (c == Byte.TYPE) res.append("B"); + else if (c == Character.TYPE) res.append("C"); + else if (c == Short.TYPE) res.append("S"); + else if (c == Integer.TYPE) res.append("I"); + 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 { + res.append("L"); + res.append(c.getName().replace('.', '_')); + 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 { + throw new RuntimeException( + "Could not determine JNI type for Java class \"" + + javaType.getName() + "\"; was not String or primitive"); + } + } + + private void emitOutOfMemoryCheck(PrintWriter writer, String varName, + String errorMessage) + { + writer.print(" if ("); + writer.print(varName); + writer.println(" == NULL) {"); + writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryException\"),"); + writer.print(" \"" + errorMessage); + writer.print(" in native dispatcher for \\\""); + writer.print(binding.getName()); + writer.println("\\\"\");"); + writer.print(" return"); + if (!binding.getJavaReturnType().isVoid()) { + writer.print(" 0"); + } + writer.println(";"); + writer.println(" }"); + } + + private void emitMalloc(PrintWriter writer, + String targetVarName, + String elementTypeString, + String numElementsExpression, + String mallocFailureErrorString) + { + writer.print(" "); + writer.print(targetVarName); + writer.print(" = ("); + writer.print(elementTypeString); + writer.print(" *) malloc("); + writer.print(numElementsExpression); + writer.print(" * sizeof("); + writer.print(elementTypeString); + writer.println("));"); + // Catch memory allocation failure + if (EMIT_NULL_CHECKS) { + emitOutOfMemoryCheck( + writer, targetVarName, + mallocFailureErrorString); + } + } + + private void emitGetStringUTFChars(PrintWriter writer, + String sourceVarName, + String receivingVarName) + { + writer.print(" "); + writer.print(receivingVarName); + writer.print(" = (*env)->GetStringUTFChars(env, "); + writer.print(sourceVarName); + writer.println(", (jboolean*)NULL);"); + // Catch memory allocation failure in the event that the VM didn't pin + // the String and failed to allocate a copy + if (EMIT_NULL_CHECKS) { + emitOutOfMemoryCheck( + writer, receivingVarName, + "Failed to get UTF-8 chars for argument \\\""+sourceVarName+"\\\""); + } + } + + // 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 + // an extra variable named XXX_copy (where XXX is the value of the + // cVariableName argument) will be emitted and TRUE will be returned. + private boolean emitPointerDeclaration(PrintWriter writer, + JavaType javaType, + Type cType, + String cVariableName) { + String ptrTypeString = null; + boolean needsDataCopy = false; + + // Emit declaration for the pointer variable. + // + // Note that we don't need to obey const/volatile for outgoing arguments + // + if (javaType.isNIOBuffer()) + { + ptrTypeString = cType.getName(); + } + else if (javaType.isArray()) { + // It's an array; get the type of the elements in the array + Class elementType = javaType.getJavaClass().getComponentType(); + if (elementType.isPrimitive()) + { + ptrTypeString = cType.getName(); + } + else if (elementType == java.lang.String.class) + { + ptrTypeString = "jstring *"; + needsDataCopy = true; + } + else if (elementType.isArray()) + { + needsDataCopy = true; + + 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 something we can't/don't handle + throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + } + } + else + { + ptrTypeString = cType.getName(); + } + + // declare the pointer variable + writer.print(" "); + writer.print(ptrTypeString); + writer.print(" "); + writer.print(cVariableName); + writer.println(" = NULL;"); + + if (needsDataCopy) + { + // 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) { + writer.print(" char **"); + } else { + writer.print(ptrTypeString); + } + writer.print(" "); + writer.print(cVariableName); + writer.print("_copy = NULL; /* copy of data in "); + writer.print(cVariableName); + writer.println(", laid out according to C memory model */"); + } + + return needsDataCopy; + } + + private void emitPointerConversion(PrintWriter writer, + MethodBinding binding, + JavaType type, + Type cType, + String incomingArgumentName, + String cVariableName) { + 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(");"); + + writer.print(" if ("); + writer.print(cVariableName); + writer.println(" == NULL) {"); + writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/RuntimeException\"),"); + writer.print (" \"Argument \\\""); + writer.print(incomingArgumentName); + writer.println("\\\" was not a direct buffer\");"); + writer.print (" return"); + if (!binding.getJavaReturnType().isVoid()) { + writer.print(" 0"); + } + writer.println(";"); + writer.println(" }"); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + + protected String pointerConversionArgumentName(int i) { + return "_ptr" + i; + } + + /** + * Class that emits a generic comment for CMethodBindingEmitters; the comment + * includes the C signature of the native method that is being bound by the + * emitter java method. + */ + protected static class DefaultCommentEmitter implements CommentEmitter { + public void emit(FunctionEmitter emitter, PrintWriter writer) { + emitBeginning((CMethodBindingEmitter)emitter, writer); + emitEnding((CMethodBindingEmitter)emitter, writer); + } + protected void emitBeginning(CMethodBindingEmitter emitter, PrintWriter writer) { + writer.println(" Java->C glue code:"); + writer.print(" * Java package: "); + writer.print(emitter.getJavaPackageName()); + writer.print("."); + writer.println(emitter.getJavaClassName()); + writer.print(" * Java method: "); + MethodBinding binding = emitter.getBinding(); + writer.println(binding); + writer.println(" * C function: " + binding.getCSymbol()); + } + protected void emitEnding(CMethodBindingEmitter emitter, PrintWriter writer) { + } + } + +} + |