aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/games/gluegen/CMethodBindingEmitter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java/games/gluegen/CMethodBindingEmitter.java')
-rw-r--r--src/net/java/games/gluegen/CMethodBindingEmitter.java1141
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) {
+ }
+ }
+
+}
+