diff options
Diffstat (limited to 'src/java/com/sun/gluegen/JavaMethodBindingEmitter.java')
-rw-r--r-- | src/java/com/sun/gluegen/JavaMethodBindingEmitter.java | 873 |
1 files changed, 873 insertions, 0 deletions
diff --git a/src/java/com/sun/gluegen/JavaMethodBindingEmitter.java b/src/java/com/sun/gluegen/JavaMethodBindingEmitter.java new file mode 100644 index 0000000..d7fa7c4 --- /dev/null +++ b/src/java/com/sun/gluegen/JavaMethodBindingEmitter.java @@ -0,0 +1,873 @@ +/* + * 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 com.sun.gluegen; + +import java.io.*; +import java.util.*; +import java.text.MessageFormat; + +import com.sun.gluegen.cgram.types.*; +import com.sun.gluegen.cgram.*; + +/** + * An emitter that emits only the interface for a Java<->C JNI binding. + */ +public class JavaMethodBindingEmitter extends FunctionEmitter +{ + public static final EmissionModifier PUBLIC = new EmissionModifier("public"); + public static final EmissionModifier PROTECTED = new EmissionModifier("protected"); + public static final EmissionModifier PRIVATE = new EmissionModifier("private"); + public static final EmissionModifier ABSTRACT = new EmissionModifier("abstract"); + public static final EmissionModifier FINAL = new EmissionModifier("final"); + public static final EmissionModifier NATIVE = new EmissionModifier("native"); + public static final EmissionModifier SYNCHRONIZED = new EmissionModifier("synchronized"); + + 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; + private String unsupportedExceptionType; + + protected boolean emitBody; + protected boolean eraseBufferAndArrayTypes; + protected boolean directNIOOnly; + protected boolean forImplementingMethodCall; + protected boolean forDirectBufferImplementation; + protected boolean forIndirectBufferAndArrayImplementation; + protected boolean isUnimplemented; + protected boolean tagNativeBinding; + + protected MethodBinding binding; + + // Manually-specified prologue and epilogue code + protected List/*<String>*/ prologue; + protected List/*<String>*/ epilogue; + + // A non-null value indicates that rather than returning a compound + // type accessor we are returning an array of such accessors; this + // expression is a MessageFormat string taking the names of the + // incoming Java arguments as parameters and computing as an int the + // number of elements of the returned array. + private String returnedArrayLengthExpression; + + // A suffix used to create a temporary outgoing array of Buffers to + // represent an array of compound type wrappers + private static final String COMPOUND_ARRAY_SUFFIX = "_buf_array_copy"; + + // Only present to provide more clear comments + private JavaConfiguration cfg; + + public JavaMethodBindingEmitter(MethodBinding binding, + PrintWriter output, + String runtimeExceptionType, + String unsupportedExceptionType, + boolean emitBody, + boolean tagNativeBinding, + boolean eraseBufferAndArrayTypes, + boolean directNIOOnly, + boolean forImplementingMethodCall, + boolean forDirectBufferImplementation, + boolean forIndirectBufferAndArrayImplementation, + boolean isUnimplemented, + boolean isInterface, + JavaConfiguration configuration) + { + super(output, isInterface); + this.binding = binding; + this.runtimeExceptionType = runtimeExceptionType; + this.unsupportedExceptionType = unsupportedExceptionType; + this.emitBody = emitBody; + this.tagNativeBinding = tagNativeBinding; + 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); + } + cfg = configuration; + } + + public JavaMethodBindingEmitter(JavaMethodBindingEmitter arg) { + super(arg); + binding = arg.binding; + runtimeExceptionType = arg.runtimeExceptionType; + unsupportedExceptionType = arg.unsupportedExceptionType; + emitBody = arg.emitBody; + tagNativeBinding = arg.tagNativeBinding; + eraseBufferAndArrayTypes = arg.eraseBufferAndArrayTypes; + directNIOOnly = arg.directNIOOnly; + forImplementingMethodCall = arg.forImplementingMethodCall; + forDirectBufferImplementation = arg.forDirectBufferImplementation; + forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation; + isUnimplemented = arg.isUnimplemented; + prologue = arg.prologue; + epilogue = arg.epilogue; + returnedArrayLengthExpression = arg.returnedArrayLengthExpression; + cfg = arg.cfg; + } + + public final MethodBinding getBinding() { return binding; } + + public boolean isForImplementingMethodCall() { return forImplementingMethodCall; } + public boolean isForDirectBufferImplementation() { return forDirectBufferImplementation; } + public boolean isForIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } + + public String getName() { + return binding.getRenamedMethodName(); + } + + protected String getArgumentName(int i) { + return binding.getArgumentName(i); + } + + /** The type of exception (must subclass + <code>java.lang.RuntimeException</code>) raised if runtime + checks fail in the generated code. */ + public String getRuntimeExceptionType() { + return runtimeExceptionType; + } + + public String getUnsupportedExceptionType() { + return unsupportedExceptionType; + } + + /** If the underlying function returns an array (currently only + arrays of compound types are supported) as opposed to a pointer + to an object, this method should be called to provide a + MessageFormat string containing an expression that computes the + number of elements of the returned array. The parameters to the + MessageFormat expression are the names of the incoming Java + arguments. */ + public void setReturnedArrayLengthExpression(String expr) { + returnedArrayLengthExpression = expr; + } + + /** Sets the manually-generated prologue code for this emitter. */ + public void setPrologue(List/*<String>*/ prologue) { + this.prologue = prologue; + } + + /** Sets the manually-generated epilogue code for this emitter. */ + public void setEpilogue(List/*<String>*/ epilogue) { + this.epilogue = epilogue; + } + + /** 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; + } + + /** Accessor for subclasses. */ + public void setForDirectBufferImplementation(boolean direct) { + this.forDirectBufferImplementation = direct; + } + + /** Accessor for subclasses. */ + public void setForIndirectBufferAndArrayImplementation(boolean indirect) { + this.forIndirectBufferAndArrayImplementation = indirect; + } + + protected void emitReturnType(PrintWriter writer) + { + writer.print(getReturnTypeString(false)); + } + + protected String erasedTypeString(JavaType type, boolean skipBuffers) { + if (eraseBufferAndArrayTypes) { + if (type.isNIOBuffer()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Object"; + } + if (!type.isNIOByteBuffer()) { + // Return buffer requiring change of view from ByteBuffer to e.g. LongBuffer + return "java.nio.ByteBuffer"; + } + } else if (type.isPrimitiveArray()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Object"; + } + } else if (type.isNIOBufferArray()) { + // Arrays of direct Buffers sent down as Object[] + // (Note we don't yet support returning void**) + return "Object[]"; + } else if (type.isCompoundTypeWrapper()) { + // Compound type wrappers are unwrapped to ByteBuffer + return "java.nio.ByteBuffer"; + } else if (type.isArrayOfCompoundTypeWrappers()) { + if (skipBuffers) { + return "java.nio.ByteBuffer"; + } else { + // In the case where this is called with a false skipBuffers + // argument we want to erase the array of compound type + // wrappers to ByteBuffer[] + return "java.nio.ByteBuffer[]"; + } + } + } + if (type.isArrayOfCompoundTypeWrappers()) { + // We don't want to bake the array specification into the type name + return type.getName() + "[]"; + } + return type.getName(); + } + + protected String getReturnTypeString(boolean skipArray) { + // 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 erasedTypeString(binding.getJavaReturnType(), true) + "[]"; + } + + protected void emitName(PrintWriter writer) + { + if (forImplementingMethodCall) { + if (forIndirectBufferAndArrayImplementation) { + writer.print(getImplMethodName(false)); + } else { + writer.print(getImplMethodName(true)); + } + } else { + writer.print(getName()); + } + } + + protected int emitArguments(PrintWriter writer) + { + boolean needComma = false; + int numEmitted = 0; + + if (forImplementingMethodCall && binding.hasContainingType()) { + // Always emit outgoing "this" argument + writer.print("java.nio.ByteBuffer "); + writer.print(javaThisArgumentName()); + ++numEmitted; + needComma = true; + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + 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. + if (binding.getNumArguments() != 1) { + throw new InternalError( + "\"void\" argument type found in " + + "multi-argument function \"" + binding + "\""); + } + continue; + } + + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (needComma) { + writer.print(", "); + } + + writer.print(erasedTypeString(type, false)); + writer.print(" "); + writer.print(getArgumentName(i)); + + ++numEmitted; + needComma = true; + + // 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 offset argument after each primitive array + if (type.isPrimitiveArray()) { + writer.print(", int " + offsetArgName(i)); + } + } + return numEmitted; + } + + + protected String getImplMethodName(boolean direct) { + if (direct) { + return binding.getRenamedMethodName() + "0"; + } else { + return binding.getRenamedMethodName() + "1"; + } + } + + protected String byteOffsetArgName(int i) { + return byteOffsetArgName(getArgumentName(i)); + } + + protected String byteOffsetArgName(String s) { + return s + "_byte_offset"; + } + + protected String byteOffsetArrayArgName(int i) { + return getArgumentName(i) + "_byte_offset_array"; + } + + protected String offsetArgName(int i) { + return getArgumentName(i) + "_offset"; + } + + protected void emitBody(PrintWriter writer) + { + if (!emitBody) { + writer.println(';'); + } else { + MethodBinding binding = getBinding(); + writer.println(); + writer.println(" {"); + if (isUnimplemented) { + writer.println(" throw new " + getUnsupportedExceptionType() + "(\"Unimplemented\");"); + } else { + emitPrologueOrEpilogue(prologue, writer); + emitPreCallSetup(binding, writer); + //emitReturnVariableSetup(binding, writer); + emitReturnVariableSetupAndCall(binding, writer); + } + writer.println(" }"); + } + } + + protected void emitPrologueOrEpilogue(List/*<String>*/ code, PrintWriter writer) { + if (code != null) { + String[] argumentNames = argumentNameArray(); + for (Iterator iter = code.iterator(); iter.hasNext(); ) { + String str = (String) iter.next(); + try { + MessageFormat fmt = new MessageFormat(str); + writer.println(" " + fmt.format(argumentNames)); + } catch (IllegalArgumentException e) { + // (Poorly) handle case where prologue / epilogue contains blocks of code with braces + writer.println(" " + str); + } + } + } + } + + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + emitArrayLengthAndNIOBufferChecks(binding, writer); + emitCompoundArrayCopies(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 (" + getArgumentName(i) + ".length < " + + arrayType.getLength() + ")"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Length of array \\\"" + 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(" + getArgumentName(i) + "))"); + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Argument \\\"" + + getArgumentName(i) + "\\\" was not a direct buffer\");"); + } else { + if(firstBuffer) { + firstBuffer = false; + writer.println(" boolean _direct = BufferFactory.isDirect(" + getArgumentName(i) + ");"); + } else { + writer.println(" if (" + getArgumentName(i) + " != null && _direct != BufferFactory.isDirect(" + getArgumentName(i) + "))"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Argument \\\"" + 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 = 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 \\\"" + + 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 = 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 emitCompoundArrayCopies(MethodBinding binding, PrintWriter writer) { + // If the method binding uses outgoing arrays of compound type + // wrappers, we need to generate a temporary copy of this array + // into a ByteBuffer[] for processing by the native code + if (binding.signatureUsesArraysOfCompoundTypeWrappers()) { + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaType = binding.getJavaArgumentType(i); + if (javaType.isArrayOfCompoundTypeWrappers()) { + String argName = getArgumentName(i); + String tempArrayName = argName + COMPOUND_ARRAY_SUFFIX; + writer.println(" ByteBuffer[] " + tempArrayName + " = new ByteBuffer[" + argName + ".length];"); + writer.println(" for (int _ctr = 0; _ctr < + " + argName + ".length; _ctr++) {"); + writer.println(" " + javaType.getName() + " _tmp = " + argName + "[_ctr];"); + writer.println(" " + tempArrayName + "[_ctr] = ((_tmp == null) ? null : _tmp.getBuffer());"); + writer.println(" }"); + } + } + } + } + + 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.isNIOBuffer()) { + writer.println("java.nio.ByteBuffer _res;"); + needsResultAssignment = true; + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("java.nio.ByteBuffer[] _res;"); + needsResultAssignment = true; + } else if (((epilogue != null) && (epilogue.size() > 0)) || + binding.signatureUsesArraysOfCompoundTypeWrappers()) { + emitReturnType(writer); + writer.println(" _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.print(";"); + writer.println(); + } else { + emitCall(binding, writer, true); + writer.print(";"); + } + + 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.print(";"); + writer.println(); + writer.println(" }"); + } else { + writer.println(); + } + emitPostCallCleanup(binding, writer); + emitPrologueOrEpilogue(epilogue, writer); + 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(" + getArgumentName(i) + ")"); + } else if (type.isArrayOfCompoundTypeWrappers()) { + writer.print(getArgumentName(i) + COMPOUND_ARRAY_SUFFIX); + } else { + writer.print(getArgumentName(i)); + } + + if (type.isCompoundTypeWrapper()) { + writer.print(" == null) ? null : "); + writer.print(getArgumentName(i)); + writer.print(".getBuffer())"); + } + if (type.isNIOBuffer()) { + if (direct) { + writer.print(", BufferFactory.getDirectBufferByteOffset(" + getArgumentName(i) + ")"); + } else { + writer.print(", BufferFactory.getIndirectBufferByteOffset(" + 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 " + + getArgumentName(i) + + " -- error occurred while processing Java glue code for " + getName()); + } + writer.print(offsetArgName(i)); + } + + needComma = true; + ++numArgsEmitted; + } + return numArgsEmitted; + } + + protected void emitPostCallCleanup(MethodBinding binding, PrintWriter writer) { + if (binding.signatureUsesArraysOfCompoundTypeWrappers()) { + // For each such array, we need to take the ByteBuffer[] that + // came back from the C method invocation and wrap the + // ByteBuffers back into the wrapper types + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + if (javaArgType.isArrayOfCompoundTypeWrappers()) { + String argName = binding.getArgumentName(i); + writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); + writer.println(" if ((" + argName + "[_ctr] == null && " + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr] == null) ||"); + writer.println(" (" + argName + "[_ctr] != null && " + argName + "[_ctr].getBuffer() == " + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr])) {"); + writer.println(" // No copy back needed"); + writer.println(" } else {"); + writer.println(" if (" + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr] == null) {"); + writer.println(" " + argName + "[_ctr] = null;"); + writer.println(" } else {"); + writer.println(" " + argName + "[_ctr] = " + javaArgType.getName() + ".create(" + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr]);"); + writer.println(" }"); + writer.println(" }"); + writer.println(" }"); + } + } + } + } + + 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 " + returnType.getName() + ".create(BufferFactory.nativeOrder(_res))"); + } else { + writer.println(" BufferFactory.nativeOrder(_res);"); + String expr = new MessageFormat(fmt).format(argumentNameArray()); + 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 " + 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 * " + getReturnTypeString(true) + ".size());"); + writer.println(" _res.limit ((1 + _count) * " + getReturnTypeString(true) + ".size());"); + writer.println(" java.nio.ByteBuffer _tmp = _res.slice();"); + writer.println(" BufferFactory.nativeOrder(_tmp);"); + writer.println(" _res.position(0);"); + writer.println(" _res.limit(_res.capacity());"); + writer.println(" _retarray[_count] = " + getReturnTypeString(true) + ".create(_tmp);"); + writer.println(" }"); + writer.print (" return _retarray"); + } + writer.println(";"); + } else if (returnType.isNIOBuffer()) { + writer.println(" if (_res == null) return null;"); + writer.println(" BufferFactory.nativeOrder(_res);"); + if (!returnType.isNIOByteBuffer()) { + // See whether we have to expand pointers to longs + if (getBinding().getCReturnType().pointerDepth() >= 2) { + if (!returnType.isNIOLongBuffer()) { + throw new RuntimeException("While emitting glue code for " + getName() + + ": can not legally make pointers opaque to anything but longs"); + } + writer.println(" return BufferFactory.asPointerBuffer(_res);"); + } else { + String returnTypeName = returnType.getName().substring("java.nio.".length()); + writer.println(" return _res.as" + returnTypeName + "();"); + } + } else { + writer.println(" return _res;"); + } + } 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] = " + getReturnTypeString(true) + ".create(_res[_count]);"); + writer.println(" }"); + writer.println(" return _retarray;"); + } else { + // Assume it's a primitive type or other type we don't have to + // do any conversion on + writer.println(" return _res;"); + } + } + + protected String[] argumentNameArray() { + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = getArgumentName(i); + if (binding.getJavaArgumentType(i).isPrimitiveArray()) { + // Add on _offset argument in comma-separated expression + argumentNames[i] = argumentNames[i] + ", " + offsetArgName(i); + } + } + return argumentNames; + } + + public static String javaThisArgumentName() { + return "jthis0"; + } + + protected String getCommentStartString() { return "/** "; } + + protected String getBaseIndentString() { return " "; } + + protected String getReturnedArrayLengthExpression() { + return returnedArrayLengthExpression; + } + + /** + * Class that emits a generic comment for JavaMethodBindingEmitters; the comment + * includes the C signature of the native method that is being bound by the + * emitter java method. + */ + protected class DefaultCommentEmitter implements CommentEmitter { + public void emit(FunctionEmitter emitter, PrintWriter writer) { + emitBeginning(emitter, writer); + emitBindingCSignature(((JavaMethodBindingEmitter)emitter).getBinding(), writer); + emitEnding(emitter, writer); + } + protected void emitBeginning(FunctionEmitter emitter, PrintWriter writer) { + writer.print("Entry point to C language function: "); + } + protected void emitBindingCSignature(MethodBinding binding, PrintWriter writer) { + writer.print("<code> "); + writer.print(binding.getCSymbol().toString(tagNativeBinding)); + writer.print(" </code> "); + } + 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. + if (type.isEnum() && type.getName() != HeaderParser.ANONYMOUS_ENUM_NAME) { + EnumType enumType = (EnumType)type; + writer.println(); + writer.print(emitter.getBaseIndentString()); + writer.print(" "); + writer.print("@param "); + writer.print(getArgumentName(i)); + writer.print(" valid values are: <code>"); + for (int j = 0; j < enumType.getNumEnumerates(); ++j) { + if (j>0) writer.print(", "); + 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(getArgumentName(i)); + writer.print(" a direct {@link " + javaType.getName() + "}"); + } + } + } + } + + protected class InterfaceCommentEmitter + extends JavaMethodBindingEmitter.DefaultCommentEmitter + { + protected void emitBeginning(FunctionEmitter emitter, + PrintWriter writer) { + writer.print("Interface to C language function: <br> "); + } + } +} + |